低批
扣要戳中間才會出來
複習01背包
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 104> A;
array<int, 100004> dp;
bool bag(int n, int c){
for(int j = 0; j < n; j++){
for(int i = c; i >= A[j]; i--){
dp[i] = max(dp[i], dp[i - A[j]] + A[j]);
}
}
return dp[c] == c;
}
signed main(){
int m, n, sum;
cin >> m >> n;
while(m--){
sum = 0;
for(int &d : dp) d = 0;
for(int i = 0; i < n; i++){
cin >> A[i];
sum += A[i];
}
if(sum & 1) cout << "No\n";
else cout << (bag(n, sum / 2)? "Yes\n" : "No\n");
}
return 0;
}
無限背包
每個物品數量無限
狀態
dp(i, j) = i \ weight, first \ j \ items, max \ value
01背包轉移式
dp(i, j) = max(dp(i, j - 1), dp(i - w_j, j - 1) + v_j)
無限背包轉移式
dp(i, j) = max(dp(i, j - 1), dp(i - w_j, j) + v_j)
複雜度?
狀態數 :
轉移時間 :
O(NC)
O(1)
O(NC)
扣的
array<int, 1004> W, V;
array<array<int, 1004>, 1004> dp;
int infbag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int i = W[j]; i <= c; i++){
dp[i][j] = max(dp[i][j - 1], dp[i - W[j]][j] + V[j]);
ans = max(ans, dp[i][j]);
}
}
return ans;
}
記憶體比較小的扣的
array<int, 1004> W, V, dp;
int infbag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int i = W[j]; i <= c; i++){
dp[i] = max(dp[i], dp[i - W[j]] + V[j]);
ans = max(ans, dp[i]);
}
}
return ans;
}
裸題
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 1004> A, D;
array<int, 10004> dp;
bool infbag(int w, int b, int n){
for(int j = 0; j < n; j++){
for(int i = A[j]; i < w; i++){
dp[i] = max(dp[i], dp[i - A[j]] + D[j]);
if(dp[i] >= b) return 1;
}
}
return 0;
}
signed main(){
int t, w, b, n, m;
cin >> t;
while(t--){
cin >> w >> b >> n;
for(int &d : dp) d = 0;
for(int i = 0; i < n; i++){
cin >> A[i] >> D[i];
}
cin >> m;
for(int i = 0; i < n; i++){
A[i] += m;
}
cout << (infbag(w, b, n)? "Yes\n" : "No\n");
}
return 0;
}
湊錢問題
有n種幣值,求湊出k元的最小數量
狀態
dp_i = min \ coin \ to \ i \ dollars
轉移
dp_i = min_{0 \le j < n}(dp_{i - v_j} + 1)
複雜度
狀態數 :
轉移時間 :
O(K)
O(N)
O(NK)
扣的
array<int, 1004> V, dp;
int money(int n, int k){
for(int i = 1; i <= k; i++){
dp[i] = 1e0;
for(int j = 0; j < n; j++){
if(V[j] < i) continue;
dp[i] = min(dp[i], dp[i - V[j]] + 1);
}
}
return dp[k];
}
不打馬賽克的裸題
扣的
#include <iostream>
#include <algorithm>
using namespace std;
int V[6] = {1, 5, 10, 12, 16, 20};
int dp[104];
int money(int k){
for(int i = 1; i <= k; i++){
dp[i] = 1e9;
for(int j = 0; j < 6; j++){
if(i - V[j] < 0) break;
dp[i] = min(dp[i], dp[i - V[j]] + 1);
}
}
return dp[k];
}
signed main(){
int n;
cin >> n;
cout << money(n);
return 0;
}
分數背包
物品可以被切割
哈哈其實是greedy啦
有限背包
每個物品都有限制數量
陽春之術
拆成01背包做
O(CNK)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 54> W, M, C;
array<int, 10004> dp;
int bag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int k = 0; k < C[j]; k++){
for(int i = c; i >= W[j]; i--){
dp[i] = max(dp[i], dp[i - W[j]] + M[j]);
ans = max(ans, dp[i]);
}
}
}
return ans;
}
signed main(){
int n, t;
cin >> n;
for(int i = 0; i < n; i++){
cin >> W[i] >> M[i] >> C[i];
}
cin >> t;
cout << bag(n, t);
return 0;
}
分塊之術
1, 2, 4, 8, 16 \dots, 2^{h - 1}, K - 2^h + 1 \ (2^h \le K)
O(CNlogK)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 100004> W, M, C;
array<int, 1000004> dp;
int bag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int k = 1; k <= C[j]; k *= 2){
for(int i = c; i >= k * W[j]; i--){
dp[i] = max(dp[i], dp[i - k * W[j]] + k * M[j]);
ans = max(ans, dp[i]);
}
C[j] -= k;
}
int k = C[j];
for(int i = c; i >= k * W[j]; i--){
dp[i] = max(dp[i], dp[i - k * W[j]] + k * M[j]);
ans = max(ans, dp[i]);
}
}
return ans;
}
signed main(){
int n, t;
cin >> n;
for(int i = 0; i < n; i++){
cin >> W[i] >> M[i] >> C[i];
}
cin >> t;
cout << bag(n, t);
return 0;
}
另一種湊錢問題
給你n個不同幣值的錢,每一種都有數量,問能不能湊出c元
對每種錢幣,從小於幣值的每個數開始,每次增加幣值,然後捨去超過數量的
狀態
dp(i, j) = i \ dollar, first \ j \ coins, can?
轉移
dp(i, j) = dp(i - v_jk_j, j - 1) | dp(i - v_j(k_j - 1), j - 1) |\\ \dots | dp(i, j - 1)
複雜度
狀態 :
轉移 :
O(NM)
O(1)
O(NM)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 104> C, K;
array<array<int, 2>, 20004> dp;
int money(int n, int c){
for(int j = 0; j < n; j++){
for(int k = 0; k < C[j]; k++){
for(int i = k, l = k - C[j] * (K[j] + 1), sum = 0; i <= c; i += C[j], l += C[j]){
if(l >= 0) sum -= dp[l][1 ^ (j & 1)];
sum += dp[i][1 ^ (j & 1)];
dp[i][j & 1] = !!sum;
}
}
}
return dp[c][1 ^ (n & 1)];
}
signed main(){
int t, n, m;
cin >> t;
while(t--){
for(int i = 0; i <= m; i++){
for(int j : {0, 1}){
dp[i][j] = 0;
}
}
dp[0][0] = dp[0][1] = 1;
cin >> n >> m;
for(int i = 0; i < n; i++){
cin >> C[i] >> K[i];
}
cout << (money(n, m)? "Yes\n" : "No\n");
}
return 0;
}
LIS
Longest Increasing Subsequence
狀態
dp_i = LIS \ while \ choosing \ A_i \ as \ last \ one
轉移
dp_i = max_{j < i, A_j < A_i}(dp_j + 1)
複雜度
狀態數 :
轉移時間 :
O(N)
O(N)
O(N^2)
扣的
array<int, 1004> dp, A;
int LIS(int n){
int ans = 0;
for(int i = 0; i < n; i++){
dp[i] = 1;
for(int j = 0; j < i; j++){
if(A[i] > A[j]){
dp[i] = max(dp[i], dp[j] + 1);
}
}
ans = max(ans, dp[i]);
}
return ans;
}
優化
找到目前LIS中第一個比自己大的,然後取代他
LIS為遞增序列,因此可以二分搜
複雜度
搜尋時間 * 操作次數
O(NlogN)
扣的
#define pb push_back
vector<int> lis;
array<int, 100004> A;
int LIS(int n){
for(int i = 0; i < n; i++){
auto p = lower_bound(lis.begin(), lis.end(), A[i]);
if(p == lis.end()) lis.pb(A[i]);
else *p = A[i];
}
return lis.size();
}
題目
扣的
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
vector<int> lis;
array<int, 100004> A;
int LIS(int n){
for(int i = 0; i < n; i++){
auto p = lower_bound(lis.begin(), lis.end(), A[i]);
if(p == lis.end()) lis.pb(A[i]);
else *p = A[i];
}
return lis.size();
}
signed main(){
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> A[i];
}
cout << LIS(n);
return 0;
}
扣的
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct doll{
int h, w;
};
array<doll, 20004> D;
vector<int> lis;
bool cmp(doll a, doll b){
if(a.h != b.h) return a.h < b.h;
return a.w > b.w;
}
int LIS(int n){
lis.clear();
for(int i = 0; i < n; i++){
auto p = lower_bound(lis.begin(), lis.end(), D[i].w);
if(p == lis.end()) lis.pb(D[i].w);
else *p = D[i].w;
}
return lis.size();
}
signed main(){
int t, n;
cin >> t;
while(t--){
cin >> n;
for(int i = 0; i < n; i++){
cin >> D[i].h >> D[i].w;
}
sort(D.begin(), D.begin() + n, cmp);
cout << LIS(n) << "\n";
}
return 0;
}
扣的
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct dot{
int x, y;
};
array<dot, 200004> D;
vector<int> lis;
bool cmp(dot a, dot b){
if(a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
int LIS(int n){
lis.clear();
for(int i = 0; i < n; i++){
auto p = upper_bound(lis.begin(), lis.end(), D[i].y);
if(p == lis.end()) lis.pb(D[i].y);
else *p = D[i].y;
}
return lis.size();
}
signed main(){
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> D[i].x >> D[i].y;
}
sort(D.begin(), D.begin() + n, cmp);
cout << LIS(n);
return 0;
}
扣的
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
map<int, int> P;
array<int, 200004> A, B, D, L, lis;
array<vector<int>, 200004> S;
int lowerbound(int x){
int p = 0;
for(int i = 17; i >= 0; i--){
if(p + (1 << i) < lis.size() && lis[p + (1 << i)] > x){
p += (1 << i);
}
}
return p + 1;
}
void LIS(int n){
for(int i = n; i > 0; i--){
int p = lowerbound(D[i]);
lis[p] = D[i];
L[i] = p;
}
}
void print(int n, int now){
if(!n) return;
int nxt;
for(int s : S[n]){
if(s < now) break;
if(D[s] > D[now] && A[s] > A[nxt]) nxt = s;
}
cout << A[nxt] << " ";
print(n - 1, nxt);
}
signed main(){
int n, mx = 0;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> A[i];
P[A[i]] = i;
}
for(int i = 1; i <= n; i++){
cin >> B[i];
D[P[B[i]]] = i;
}
LIS(n);
for(int i = n; i > 0; i--){
S[L[i]].pb(i);
mx = max(mx, L[i]);
}
print(mx, 0);
return 0;
}
編輯距離
送你兩個字串,有三種操作 : 刪掉一個字,加入一個字,更改一個字。求把兩個字串變一樣的最少操作次數
狀態
dp(i, j) = min \ operations \ when \\ first \ i \ char \ on \ first \\ string \ and \ first \ j \\ char \ on \ second \ string
轉移
dp(i, j) = dp(i - 1, j - 1), if(S_{1i} == S_{2j})\\
dp(i, j) = min(dp(i, j - 1), dp(i - 1. j), dp(i - 1, j - 1)) + 1
複雜度
狀態數 :
轉移時間 :
O(S_1sizeS_2size)
O(1)
O(S_1sizeS_2size)
題目
扣的
#include <bits/stdc++.h>
using namespace std;
string A, B;
array<array<int, 1004>, 1004> dp;
int ED(int i, int j){
if(i == 0 || j == 0) return i? i : j;
if(dp[i][j]) return dp[i][j];
if(A[i - 1] == B[j - 1]) return dp[i][j] = ED(i - 1, j - 1);
return dp[i][j] = min({ED(i - 1, j), ED(i, j - 1), ED(i - 1, j - 1)}) + 1;
}
signed main(){
getline(cin, A);
getline(cin, B);
cout << ED(A.size(), B.size());
return 0;
}
位元低批
位元
二進位的位數
1 0 0 0 0
16
| |
位元運算
~ | not | 變反的 |
& | and | 和 |
| | or | 或 |
^ | xor | 相加不進位 |
<< | left shift | 左移 |
>> | right shift | 右移 |
位元枚舉
直接看題目
對每個位置枚舉0 1,
然後算答案
複雜度
狀態數 :
算答案 :
O(NK2^N)
O(2^N)
O(NK)
扣的
#include <bits/stdc++.h>
using namespace std;
array<array<int, 16>, 3> S;
void run(int n, int k){
array<int, 3> dif;
int now, ans, sml, big = 0;
for(int i = 0; i < 1 << n; i++){
sml= 1e9;
for(int l = 0; l < k; l++){
dif[l] = 0;
}
for(int j = 0; j < n; j++){
now = (i >> j) & 1;
for(int l = 0; l < k; l++){
if(S[l][j] != now) dif[l]++;
}
}
for(int l = 0; l < k; l++){
sml = min(sml, dif[l]);
}
if(sml > big){
big = sml;
ans = i;
}
}
for(int j = 0; j < n; j++){
cout << ((ans >> j) & 1);
}
}
signed main(){
int n, k;
char c;
cin >> n >> k;
if(n > 15) return 0;
for(int i = 0; i < k; i++){
for(int j = 0; j < n; j++){
cin >> c;
S[i][j] = (c ^ '0');
}
}
run(n, k);
return 0;
}
好土低批
直接看題目
狀態
dp(i, j) = min \ time \ to \ finish \ at \ j \ in\ set \ i
轉移
dp(i, j) = min_{k \subset i}(dp(i - 2^j, k) + wait(k, j))
複雜度
狀態 :
轉移 :
O(N2^N)
O(N)
O(N^22^N)
扣的
#include <bits/stdc++.h>
using namespace std;
array<array<int, 16>, 1 << 16> dp;
array<int, 16> T;
int up(int t, int x){
if(t % x == 0) return t;
return x * (t / x + 1);
}
int cost(int t, int i, int j){
t += abs(j - i);
t = up(t, T[j]);
return t;
}
int DP(int n){
int ans = 1e9;
for(int i = 1; i < 1 << n; i++){
for(int j = 0; j < n; j++){
if(!(i & (1 << j))) continue;
if(i ^ (1 << j)) dp[i][j] = 1e9;
for(int k = 0; k < n; k++){
if(k == j || !(i & (1 << k))) continue;
dp[i][j] = min(dp[i][j], cost(dp[i ^ (1 << j)][k], k, j));
}
}
}
for(int i = 0; i < n; i++){
ans = min(ans, dp[(1 << n) - 1][i]);
}
return ans;
}
signed main(){
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> T[i];
dp[1 << i][i] = T[i];
}
cout << DP(n);
return 0;
}
題目
扣的
#include <bits/stdc++.h>
using namespace std;
array<array<int, 15>, 15> A, V;
array<int, 1 << 15> dp;
int DP(int n){
int ans = 0, t;
for(int s = (1 << n) - 1; s >= 0; s--){
for(int i = 0; i < n; i++){
if(!(s & (1 << i))) continue;
for(int j = i + 1; j < n; j++){
if(!(s & (1 << j))) continue;
t = s ^ (1 << i) ^ (1 << j) ^ (1 << A[i][j]);
dp[t] = max(dp[t], dp[s] + V[i][j]);
}
}
}
for(int i = 1; i < (1 << n); i <<= 1){
ans = max(ans, dp[i]);
}
return ans;
}
signed main(){
int n;
while(cin >> n){
for(int &d : dp) d = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> V[i][j];
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> A[i][j];
}
}
cout << DP(n) << "\n";
}
return 0;
}
扣的
#include <bits/stdc++.h>
using namespace std;
int n;
array<int, 1 << 21> dp;
array<array<int, 21>, 21> R;
int lownotbit(int x){
for(int i = 0; i < n; i++){
if(!(x & (1 << i))) return i;
}
}
int DP(){
int i, t;
for(int s = 0; s < (1 << n); s++){
if(dp[s] == 1e9) continue;
i = lownotbit(s);
for(int j = i + 1; j < n; j++){
if(s & (1 << j)) continue;
for(int k = j + 1; k < n; k++){
if(s & (1 << k)) continue;
t = s | (1 << i) | (1 << j) | (1 << k);
dp[t] = min(dp[t], dp[s] + R[i][j] + R[j][k] + R[i][k]);
}
}
}
return dp[(1 << n) - 1];
}
signed main(){
int t;
cin >> t;
while(t--){
cin >> n;
for(int i = 1; i < (1 << n); i++) dp[i] = 1e9;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> R[i][j];
}
}
cout << DP() << "\n";
}
return 0;
}
扣的
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
int n;
array<double, 24> X, Y;
array<int, (1 << 24)> dp;
array<array<int, 24>, 24> line;
int highbit(int x){
int p = 0;
for(int i = 4; i >= 0; i--){
if(((1 << (p + (1 << i)))) <= x) p += (1 << i);
}
return p;
}
int DP(int n){
int k;
for(int i = 1; i < 1 << n; i++){
dp[i] = n;
k = highbit(i);
for(int j = 0; j < n; j++){
dp[i] = min(dp[i], dp[i & ~line[k][j]] + 1);
}
}
return dp[(1 << n) - 1];
}
signed main(){
int t;
double a, b;
cin >> t;
while(t--){
cin >> n;
for(int i = 0; i < n; i++){
cin >> X[i] >> Y[i];
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
line[i][j] = (1 << i);
if(X[i] == X[j]) continue;
/*
{yi = axi^2 + bxi ----- 1
{yj = axj^2 + bxj ----- 2
a :
1 - 2 :
yj(xi / xj) = axjxi + bxi
yi - yj(xi / xj) = axi^2 - axjxi
yi - yj(xi / xj) = a(xi^2 - xixj)
a = (yi - yj(xi / xj)) / (xi^2 - xixj)
a = (yixj - yjxi) / xixj(xi-xj)
b :
yi / xi = axi + b
b = yi / xi - axi
*/
a = (Y[i] * X[j] - Y[j] * X[i]) / (X[i] * X[j] * (X[i] - X[j]));
b = (Y[i] / X[i]) - (a * X[i]);
if(a >= 0) continue;
for(int k = 0; k < n; k++){
if(Y[k] - (a * X[k] * X[k] + b * X[k]) < eps && Y[k] - (a * X[k] * X[k] + b * X[k]) > -eps){
line[i][j] |= (1 << k);
}
}
}
}
cout << DP(n) << "\n";
}
return 0;
}
大背包
W那麼大,怎麼辦
把背包反過來做啊
dp(i, j) = i \ value, first \ j \ items, min \ weight
狀態
dp(i, j) = min(dp(i, j - 1), dp(i - v_j, j - 1) + w_j)
轉移
複雜度
狀態 :
轉移 :
O(N\sum V)
O(1)
O(N\sum V)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 104> W, V;
array<int, 100004> dp;
int bag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int i = 100000; i >= V[j]; i--){
dp[i] = min(dp[i], dp[i - V[j]] + W[j]);
if(dp[i] <= c) ans = max(ans, i);
}
}
return ans;
}
signed main(){
int n, w;
cin >> n >> w;
for(int &d : dp) d = w + 1;
dp[0] = 0;
for(int i = 0; i < n; i++){
cin >> W[i] >> V[i];
}
cout << bag(n, w);
return 0;
}
T跟A都那麼大,怎麼辦
跟上一題一樣?
這題是問最大耶
切兩半位元枚舉
最後再二分搜
N \le 40 !!!!
複雜度
狀態數 :
算答案 :
排序 :
二分搜 :
O(2^{N/2})
O(N)
O(N2^{N/2})
O(N2^{N/2})
O(N2^{\frac{N}{2}})
扣的
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
array<int, 44> A;
vector<int> bag(int l, int r){
vector<int> V;
for(int i = 0; i < (1 << (r - l + 1)); i++){
V.pb(0);
for(int j = 0; j <= r - l; j++){
if(i & (1 << j)){
V[i] += A[l + j];
}
}
}
return V;
}
int BIS(vector<int> L, vector<int> R, int c){
int ans = 0;
for(int l : L){
if(c - l < R[0]) continue;
ans = max(ans, l + *--upper_bound(R.begin(), R.end(), c - l));
}
return ans;
}
signed main(){
int n, t;
vector<int> L, R;
cin >> n >> t;
for(int i = 0; i < n; i++){
cin >> A[i];
}
L = bag(0, n / 2 - 1);
R = bag(n / 2, n - 1);
sort(L.begin(), L.end());
sort(R.begin(), R.end());
cout << BIS(L, R, t);
return 0;
}
求救低批
Sum Of Subsets
\sum\limits_{k \subseteq S}^{S = 11010010}{A_k}
狀態
dp_i = \sum\limits_{k \subseteq i}{A_k}
暴力法
位元枚舉每個i的所有子集
複雜度
狀態 :
算答案 :
O(2^N)
O(2^N)
O(4^N)
扣的
array<int, 1 << 10> A, dp;
void SOS(int n){
for(int i = 0; i < (1 << n); i++){
for(int k = 0; k <= i; k++){
if(k & i == k) dp[i] += A[k];
}
}
}
強暴力法
枚舉每個i的所有子集,但是用&找
直接看扣
array<int, 1 << 16> A, dp;
void SOS(int n){
for(int i = 0; i < (1 << n); i++){
dp[i] = A[0];
for(int k = i; k > 0; k = (k - 1) & i){
dp[i] += A[k];
}
}
}
複雜度
狀態 :
算答案 :
O(2^N)
O(cnt \ of \ subsets)
複雜度
k \ bits\ do \ 2^k \ operations\\
C_k^N \ with \ k \ bits\\
\sum\limits_{k = 0}^{N}{C_k^N2^k}\\
= C_0^N2^0 + C_1^N2^1 + \dots + C_N^N2^N\\
= C_0^N2^01^N + C_1^N2^11^{N - 1} + \dots + C_N^N2^N1^0\\
= (2 + 1)^N = 3^N
低批作法
狀態
dp_i = SOS \ of \ i
轉移
array<int, 1 << 24> A, dp;
void SOS(int n){
for(int i = 0; i < (1 << n); i++){
dp[i] = A[i];
}
for(int k = 1; k < (1 << n); k <<= 1){
for(int i = 0; i < (1 << n); i++){
if(i & k) dp[i] += dp[i ^ k];
}
}
}
複雜度
狀態 :
轉移 :
O(2^N)
O(N)
O(N2^N)
題目
扣的
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 1 << 22> dp;
void SOS(int n){
for(int k = 1; k < (1 << n); k <<= 1){
for(int i = 1; i < (1 << n); i++){
if(i & k) dp[i] += dp[i ^ k];
}
}
}
signed main(){
int n, m, s, v, ans = 0;
char t;
cin >> n >> m;
while(m--){
s = 0;
for(int i = 0; i < n; i++){
s <<= 1;
cin >> t;
s |= (t ^ '0');
}
cin >> v;
dp[s] = v;
}
SOS(n);
sort(dp.begin() + 1, dp.begin() + (1 << n));
for(int i = 1; i < (1 << n); i++){
ans += i * dp[i];
}
cout << ans;
return 0;
}
扣的
#include <bits/stdc++.h>
using namespace std;
int n;
array<int, 3> C;
array<int, 1 << 24> dp;
void SOS(){
for(int k = 1; k < (1 << 24); k <<= 1){
for(int i = 0; i < (1 << 24); i++){
if(i & k) dp[i] += dp[i ^ k];
}
}
}
signed main(){
int s, ans = 0, cnt;
string S;
cin >> n;
while(n--){
cin >> S;
for(int i = 1; i < 8; i++){
cnt = 0;
s = 0;
for(int j = 0; j < 3; j++){
if(i & (1 << j)){
cnt++;
s |= 1 << (S[j] - 'a');
}
}
if(cnt & 1) dp[s]++;
else dp[s]--;
}
}
SOS();
for(int i = 0; i < (1 << 24); i++){
ans ^= dp[i] * dp[i];
}
cout << ans;
return 0;
}
滾動低批
當你覺得記憶體不夠時
當你轉移都從一個記憶體不大地方過來
例
\(dp_{i, j}\) 只從 \(dp_{i - 1, k}\) 轉移
那我只要紀錄 \(i\) 跟 \(i - 1\) 就好了
一個我覺得還不錯的實作方法
for(int i = 1, p = 1; i <= n; i++, p ^= 1){
for(int j = 1; j <= m; j++){
dp[p][j] = dp[!p][?]...;
}
}
題目
扣
#include <bits/stdc++.h>
using namespace std;
array<char, 2000004> S;
array<array<int, 15>, 2> dp;
int DP(int n){
int ans = 0;
for(int i = 1, p = 1; i <= n; i++, p ^= 1){
for(int j = 0; j < 15; j++) dp[p][j] = dp[!p][j];
if(S[i] == 'C'){
dp[p][0]++; //C
dp[p][5] = max(dp[p][5], dp[p][1]) + 1; //PC
dp[p][7] = max(dp[p][7], dp[p][2]) + 1; //EC
dp[p][12] = max(dp[p][12], dp[p][6]) + 1; //PEC
dp[p][14] = max(dp[p][14], dp[p][8]) + 1; //EPC
}else if(S[i] == 'P'){
dp[p][1]++; //P
dp[p][3] = max(dp[p][3], dp[p][0]) + 1; //8wCP
dp[p][8] = max(dp[p][8], dp[p][2]) + 1; //EP
dp[p][10] = max(dp[p][10], dp[p][4]) + 1; //CEP
dp[p][13] = max(dp[p][13], dp[p][7]) + 1; //ECP
}else{
dp[p][2]++; //E
dp[p][4] = max(dp[p][4], dp[p][0]) + 1; //CE
dp[p][6] = max(dp[p][6], dp[p][1]) + 1; //PE
dp[p][9] = max(dp[p][9], dp[p][3]) + 1; //CPE
dp[p][11] = max(dp[p][11], dp[p][5]) + 1; //PCE
}
}
for(int i = 0; i < 15; i++) ans = max(ans, dp[n & 1][i]);
return ans;
}
signed main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> S[i];
cout << DP(n) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define pb push_back
#define pii pair<int, int>
#define ff first
#define ss second
using namespace std;
const int inf = 1 << 30;
array<array<array<int, 2104>, 2104>, 2> dp;
vector<pii> B;
bool cmp(pii a, pii b){
return a.ff > b.ff;
}
int DP(int n){
int p = 0, sum = 0, k, ans = inf;
for(array<array<int, 2104>, 2104> &d : dp){
for(array<int, 2104> &dd : d){
for(int &ddd : dd) ddd = inf;
}
}
dp[0][0][0] = 0;
for(auto [h, w] : B){
p ^= 1;
sum += w;
for(int i = 0; i <= sum; i++){
for(int j = 0; j <= sum; j++){
k = sum - i - j;
dp[p][i][j] = inf;
if(i >= w) dp[p][i][j] = min(dp[p][i][j], dp[!p][i - w][j] + (i == w? h : 0));
if(j >= w) dp[p][i][j] = min(dp[p][i][j], dp[!p][i][j - w] + (j == w? h : 0));
if(k >= w) dp[p][i][j] = min(dp[p][i][j], dp[!p][i][j] + (k == w? h : 0));
}
}
}
for(int i = 1; i <= sum; i++){
for(int j = 1; j <= sum; j++){
k = sum - i - j;
if(dp[p][i][j] == inf || !k) continue;
ans = min(ans, dp[p][i][j] * max({i, j, k}));
}
}
return ans;
}
signed main(){
int t, n, h, w;
cin >> t;
while(t--){
B.clear();
cin >> n;
for(int i = 1; i <= n; i++){
cin >> h >> w;
B.pb({h, w});
}
sort(B.begin(), B.end(), cmp);
cout << DP(n) << "\n";
}
return 0;
}
扣
#include <bits/stdc++.h>
using namespace std;
array<array<int, 5004>, 2> dp;
signed main(){
int l, w, g, ans;
while(cin >> l >> w){
if(!l || !w) break;
ans = 0;
for(array<int, 5004> &d : dp){
for(int &dd : d) dd = 0;
}
for(int i = 1, p = 1; i <= l; i++, p ^= 1){
for(int j = 1; j <= w; j++){
cin >> g;
if(g == 2) dp[p][j] = 0;
else dp[p][j] = min({dp[!p][j], dp[p][j - 1], dp[!p][j - 1]}) + 1;
ans = max(ans, dp[p][j]);
}
}
cout << ans * ans << "\n";
}
return 0;
}
插頭低批
當 \(N \times M\) 很小 : 16
可以位元DP耶!
0 代表沒東西,1 代表有骨牌然後爆搜放骨牌的位置
一些性質
放一個骨牌只會影響到附近幾格
已經操作過且影響不到的地方一定是放滿的
狀態
\(dp_{i, j, s} = \) 第 \(i, j\) 格,目前狀態為 \(s\) 的方法數
轉移
橫放方法數 + 直放方法數 + 不放方法數
複雜度
狀態 \(O(NM2^{min(N, M)})\) * 轉移\(O(1)\)
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
array<array<array<int, 1 << 11>, 12>, 1004> dp;
int DP(int n, int m){
dp[0][n][(1 << n + 1) - 1] = 1;
for(int i = 1; i <= m; i++){
for(int s = 0; s < (1 << n + 1); s++){
dp[i][0][s] = dp[i - 1][n][s];
}
for(int j = 1; j <= n; j++){
for(int s = 0; s < (1 << n + 1); s++){
if(s & 1){
if(s & 2 && j > 1) dp[i][j][s] += dp[i][j - 1][((s ^ 3) >> 1) ^ (1 << n)];
if(s & (1 << n)) dp[i][j][s] += dp[i][j - 1][((s ^ (1 << n)) >> 1) ^ (1 << n)];
}else dp[i][j][s] += dp[i][j - 1][(s >> 1) ^ (1 << n)];
dp[i][j][s] %= mod;
}
}
}
return dp[m][n][(1 << n + 1) - 1];
}
signed main(){
int n, m;
cin >> n >> m;
cout << DP(n, m) << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<array<int, 1 << 18>, 2> dp;
int DP(int n, int m, int k){
int p = 1, opn, ans = 0;
dp[0][(1 << (n + 2)) - 1] = 1;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++, p ^= 1){
for(int s = 0; s < 1 << (n + 2); s++){
dp[p][s] = 0;
opn = (s & 1) + !!(s & 2) + !!(s & (1 << n)) + !!(s & (1 << (n + 1)));
if(j > 1 && opn < 2) continue;
dp[p][s] += dp[!p][s >> 1];
dp[p][s] += dp[!p][(s >> 1) | (1 << (n + 1))];
dp[p][s] %= k;
}
}
}
p ^= 1;
for(int s = 1; s < 1 << (n + 2); s++){
ans = (ans + dp[p][s]) % k;
}
return ans;
}
signed main(){
int n, m, k;
cin >> n >> m >> k;
cout << DP(n, m, k) << "\n";
return 0;
}
數位低批
狀態
\(dp_{d, p, t, z} = \) 第 \(d\) 位數,第 \(d + 1\) 位數為 \(p\),前綴是否為最高限制,前綴是否為 \(0\) 的彩虹數數量
轉移
\(if(t = 1) : \) 繼續最高限制 + 非最高限制 \((num_d \ != p)\)
\(else \ if(z = 1) : \) 繼續0 + 非0
\(else : \) \(num_d \ != p\)
複雜度
狀態 \(O(Alog_AC)\) * 轉移 \(O(A)\)
假設 \(A\) 進位
扣
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
array<array<array<array<int, 2>, 2>, 10>, 20> dp;
vector<int> dig;
int DP(int d, int p, bool t, bool z){
if(d < 0) return 1;
if(dp[d][p][t][z]) return dp[d][p][t][z];
int sum = 0;
if(t){
if(z || p) sum += DP(d - 1, 0, !dig[d], z);
for(int i = 1; i <= dig[d]; i++){
if(i != p) sum += DP(d - 1, i, i == dig[d], 0);
}
}else if(z){
for(int i = 0; i <= 9; i++){
sum += DP(d - 1, i, 0, !i);
}
}else{
for(int i = 0; i <= 9; i++){
if(i != p) sum += DP(d - 1, i, 0, 0);
}
}
return dp[d][p][t][z] = sum;
}
void DEC(int x){
dig.clear();
for(int i = 0; i < 20; i++){
for(int j = 0; j < 10; j++){
for(int k : {0, 1}){
for(int l : {0, 1}) dp[i][j][k][l] = 0;
}
}
}
while(x){
dig.pb(x % 10);
x /= 10;
}
}
signed main(){
int a, b;
cin >> a >> b;
a--;
DEC(a);
if(a >= 0) a = DP(dig.size() - 1, 0, 1, 1);
DEC(b);
b = DP(dig.size() - 1, 0, 1, 1);
cout << b - max(a, 0ll) << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
#define pb push_back
#define int long long
#define pii pair<int, int>
#define ff first
#define ss second
using namespace std;
const int mod = 998244353;
int elon = 0;
array<int, 10004> ten;
array<array<bitset<1 << 10>, 2>, 10004> vis;
array<array<array<pii, 1 << 10>, 2>, 10004> dp;
vector<int> D;
pii DP(int d, bool t, int s){
if(d < 0) return {0, (s & elon) == elon};
if(vis[d][t][s]) return dp[d][t][s];
vis[d][t][s] = 1;
int sum = 0, cnt = 0;
pii tmp;
if(t){
tmp = DP(d - 1, D[d] == 0, s | !!s);
sum += tmp.ff, cnt += tmp.ss;
for(int i = 1; i <= D[d]; i++){
auto [k, c] = DP(d - 1, i == D[d], s | (1 << i));
sum = (sum + k + i * ten[d] * c) % mod;
cnt = (cnt + c) % mod;
}
}else if(!s){
tmp = DP(d - 1, 0, 0);
sum += tmp.ff, cnt += tmp.ss;
for(int i = 1; i < 10; i++){
auto [k, c] = DP(d - 1, 0, s | (1 << i));
sum = (sum + k + i * ten[d] * c) % mod;
cnt = (cnt + c) % mod;
}
}else{
for(int i = 0; i < 10; i++){
auto [k, c] = DP(d - 1, 0, s | (1 << i));
sum = (sum + k + i * ten[d] * c) % mod;
cnt = (cnt + c) % mod;
}
}
return dp[d][t][s] = {sum, cnt};
}
signed main(){
int m, c, p = 1;
string N;
cin >> N >> m;
ten[0] = 1;
for(char n : N){
D.pb(n ^ '0');
ten[p] = 10 * ten[p - 1] % mod;
p++;
}
reverse(D.begin(), D.end());
while(m--){
cin >> c;
elon |= (1 << c);
}
cout << DP(D.size() - 1, 1, 0).ff << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int m = 1e9 + 7;
int d;
array<array<array<bool, 2>, 104>, 10004> vis;
array<array<array<int, 2>, 104>, 10004> dp;
vector<int> D;
int mod(int x, int p){
return ((x % p) + p) % p;
}
int DP(int i, int j, bool t){
if(i < 0) return j == 0;
if(vis[i][j][t]) return dp[i][j][t];
vis[i][j][t] = 1;
int sum = 0;
if(t){
for(int k = 0; k <= D[i]; k++){
sum += DP(i - 1, mod(j - k, d), k == D[i]);
}
}else{
for(int k = 0; k < 10; k++){
sum += DP(i - 1, mod(j - k, d), 0);
}
}
return dp[i][j][t] = mod(sum, m);
}
signed main(){
string K;
cin >> K >> d;
for(char k : K) D.pb(k ^ '0');
reverse(D.begin(), D.end());
cout << mod(DP(D.size() - 1, 0, 1) - 1, m) << "\n";
return 0;
}
資結優化
BIT
長得很像LIS的東西
狀態
\(dp_i = \) 以第 \(i\) 個當最後一個的 IS 數量
轉移
\(dp_i = \sum_{j = 1, X_j < X_i}^{i}{dp_j} + 1\)
優化
用BIT維護前綴和
複雜度
Before : 狀態 \(O(N)\) * 轉移 \(O(N)\)
After : 狀態 \(O(N)\) * 轉移 \(O(logN)\)
扣
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
int mod = 1e9 + 7;
array<int, 200004> X, BIT;
vector<pair<int, int>> S;
void update(int p, int x){
for(; p < 200004; p += p & -p) BIT[p] = (BIT[p] + x) % mod;
}
int query(int p){
int sum = 0;
for(; p; p -= p & -p) sum = (sum + BIT[p]) % mod;
return sum;
}
void pear(vector<pair<int, int>> &V){
int lst = 0, k = 0;
sort(V.begin(), V.end());
for(auto [x, p] : V){
k += x != lst;
X[p] = k;
lst = x;
}
}
int DP(int n){
int sum = 0, t;
for(int i = 1; i <= n; i++){
t = query(X[i] - 1) + 1;
sum = (sum + t) % mod;
update(X[i], t);
}
return sum;
}
signed main(){
int n, x;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> x;
S.pb({x, i});
}
pear(S);
cout << DP(n) << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
array<int, 200004> P, Q, R, BIT;
vector<int> D;
bool cmp(int a, int b){
return R[a] > R[b];
}
void update(int p, int x){
for(; p < 200004; p += p & -p) BIT[p] = max(BIT[p], x);
}
int query(int p){
int mix = 0;
for(; p; p -= p & -p) mix = max(mix, BIT[p]);
return mix;
}
void DIV(int x){
D.clear();
for(int i = 1; i * i <= x; i++){
if(x % i == 0){
D.pb(i);
if(i * i != x) D.pb(x / i);
}
}
sort(D.begin(), D.end(), cmp);
}
int DP(int n){
for(int i = 1; i <= n; i++){
DIV(Q[i]);
for(int d : D){
update(R[d], query(R[d] - 1) + 1);
}
}
return query(n);
}
signed main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> P[i];
R[P[i]] = i;
}
for(int i = 1; i <= n; i++) cin >> Q[i];
cout << DP(n) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
using namespace std;
array<int, 800004> BIT;
void update(int p, int x){
for(; p < 800004; p += p & -p) BIT[p] += x;
}
int query(int p){
int sum = 0;
for(; p; p -= p & -p) sum += BIT[p];
return sum;
}
int find(int x){
int p = 0, sum = 0;
for(int i = 1 << 19; i; i >>= 1){
if(p + i < 800003 && sum + BIT[p + i] < x){
p += i;
sum += BIT[p];
}
}
return p + 1;
}
signed main(){
int n, s, t;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> s >> t;
s++, t++;
update(s, 1);
update(find(query(t) + 1), -1);
}
cout << query(n) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 200004> H, A, dp, BIT;
void update(int p, int x){
for(; p < 200004; p += p & -p) BIT[p] = max(BIT[p], x);
}
int query(int p){
int ans = 0;
for(; p; p -= p & -p) ans = max(ans, BIT[p]);
return ans;
}
int DP(int n){
int ans = 0;
for(int i = 1; i <= n; i++){
dp[i] = query(H[i]) + A[i];
update(H[i], dp[i]);
ans = max(ans, dp[i]);
}
return ans;
}
signed main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> H[i];
for(int i = 1; i <= n; i++) cin >> A[i];
cout << DP(n) << "\n";
return 0;
}
Trie
狀態
\(dp_i = i \ to \ N\) 的方法數
轉移
\(dp_i = \sum_{j = i}^{N}{dp_j \times cnt_{S[i, j]}}\)
優化
砸個 Trie 就可以邊跑邊算了
複雜度
狀態 \(O(N)\) * 轉移 \(O(N)\)
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
int k = 0;
array<int, 5004> dp;
array<int, 1000004> cnt;
array<array<int, 26>, 1000004> trie;
void update(int p, int i, string &S){
if(i == S.size()){
cnt[p]++;
return;
}
if(!trie[p][S[i] - 'a']) trie[p][S[i] - 'a'] = ++k;
update(trie[p][S[i] - 'a'], i + 1, S);
}
int query(int p, int i, string &S){
if(i == S.size() + 1) return 0;
int sum = cnt[p] * dp[i];
if(trie[p][S[i] - 'a']) sum += query(trie[p][S[i] - 'a'], i + 1, S);
return sum % mod;
}
signed main(){
int n, m;
string S, T;
cin >> S >> m;
n = S.size();
for(int i = 0; i < m; i++){
cin >> T;
update(0, 0, T);
}
dp[n] = 1;
for(int i = n - 1; i >= 0; i--){
dp[i] = query(0, i, S);
}
cout << dp[0] << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
using namespace std;
const int inf = 1 << 30;
int k = 1;
array<int, 2004> cnt, F;
array<array<int, 3>, 2004> trie;
array<array<int, 2004>, 10004> dp;
void update(int p, int i, string &S, int w){
if(i == S.size()){
cnt[p] += w;
return;
}
if(!trie[p][S[i]]) trie[p][S[i]] = ++k;
update(trie[p][S[i]], i + 1, S, w);
}
int fail(int p, char c){
if(trie[p][c]) return trie[p][c];
return fail(F[p], c);
}
void AC(int s){
int u;
queue<int> Q;
Q.push(s), F[s] = 0;
while(!Q.empty()){
u = Q.front();
Q.pop();
for(char i = 0; i < 3; i++){
if(!trie[u][i]) continue;
F[trie[u][i]] = fail(F[u], i);
cnt[trie[u][i]] += cnt[F[trie[u][i]]];
Q.push(trie[u][i]);
}
}
}
int DP(string &S){
int n = S.size(), p, ans = -inf;
dp[0][1] = 0;
for(int i = 2; i <= k; i++) dp[0][i] = -inf;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= k; j++) dp[i][j] = -inf;
for(int j = 1; j <= k; j++){
for(char c = 0; c < 3; c++){
if(S[i - 1] != c && S[i - 1] < 3) continue;
p = fail(j, c);
dp[i][p] = max(dp[i][p], dp[i - 1][j] + cnt[p]);
}
}
}
for(int i = 1; i <= k; i++){
ans = max(ans, dp[n][i]);
}
return ans;
}
signed main(){
int n, m, w;
string S, T;
for(int i = 0; i < 3; i++) trie[0][i] = 1;
cin >> n >> m;
for(int i = 0; i < m; i++){
cin >> T >> w;
for(char &t : T){
if(t == 'r') t = 0;
else if(t == 'g') t = 1;
else t = 2;
}
update(1, 0, T, w);
}
AC(1);
cin >> S;
for(char &s : S){
if(s == 'r') s = 0;
else if(s == 'g') s = 1;
else if(s == 'b') s = 2;
else s = 3;
}
cout << DP(S) << "\n";
return 0;
}
矩陣優化
費氏數列
狀態
\(dp_i = \) 費氏數列第 \(i\) 項
轉移
\(dp_i = dp_{i - 1} + dp_{i - 2}\)
優化
搞一個狀態矩陣 + 一個轉移矩陣
\(\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \times \begin{bmatrix} dp_{i - 1} \\ dp_{i - 2} \end{bmatrix} = \begin{bmatrix} dp_i \\ dp_{i - 1} \end{bmatrix}\)
\(\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^k \times \begin{bmatrix} dp_{i} \\ dp_{i - 1} \end{bmatrix} = \begin{bmatrix} dp_{i + k} \\ dp_{i + k - 1} \end{bmatrix}\)
那我們只要能快速的算矩陣冪就可以加速DP了
快速冪
\(x^k = (x^{\frac{k}{2}})^2\)
複雜度
Before : \(O(K)\)
After : \(O(N^3logK)\)
\(N = \) 轉移矩陣邊長
貼心提醒
要初始化
不要忘記
不要錯過
因為非常好WA
BY 一個已經被初始化搞了10次以上仍然不會記得要初始化的人
題目
扣
#include <bits/stdc++.h>
#define int long long
#define matrix array<array<int, 2>, 2>
using namespace std;
const int mod = 1e9 + 7;
matrix T, dp;
matrix mul(matrix A, matrix B){
matrix C;
for(int i : {0, 1}){
for(int j : {0, 1}){
C[i][j] = 0;
for(int k : {0, 1}){
C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
}
}
}
return C;
}
matrix exp(matrix X, int k){
matrix P;
for(int i : {0, 1}){
for(int j : {0, 1}) P[i][j] = 0;
P[i][i] = 1;
}
for(int i = 1; i <= k; i <<= 1){
if(i & k) P = mul(P, X);
X = mul(X, X);
}
return P;
}
signed main(){
int x1, x2, a, b, n;
cin >> x1 >> x2 >> a >> b >> n;
T[0][0] = b, T[0][1] = a, T[1][0] = 1;
dp[0][0] = x2, dp[1][0] = x1;
cout << mul(exp(T, n - 2), dp)[0][0] << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
#define matrix array<array<int, 204>, 204>
using namespace std;
const int no = -(1ll << 60);
int n;
array<int, 204> H;
matrix G;
matrix mul(matrix A, matrix B){
matrix C;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
C[i][j] = no;
for(int k = 1; k <= n; k++){
C[i][j] = max(C[i][j], A[i][k] + B[k][j]);
}
}
}
return C;
}
matrix exp(matrix X, int k){
matrix P;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
P[i][j] = no;
}
P[i][i] = 0;
}
for(int i = 1; i <= k; i <<= 1){
if(i & k) P = mul(P, X);
X = mul(X, X);
}
return P;
}
signed main(){
int m, s, t, a, b, ans = no;
cin >> n >> m >> s >> t;
for(int i = 1; i <= n; i++) cin >> H[i];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++) G[i][j] = no;
}
while(m--){
cin >> a >> b;
G[a][b] = H[b];
}
G = exp(G, t);
for(int i = 1; i <= n; i++){
ans = max(ans, G[s][i]);
}
cout << ans << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
#define matrix array<array<int, 3>, 3>
using namespace std;
const int mod = 998244353;
matrix T, dp;
matrix mul(matrix A, matrix B){
matrix C;
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
C[i][j] = 0;
for(int k = 0; k < 3; k++){
C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
}
}
}
return C;
}
matrix exp(matrix X, int k){
matrix P;
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++) P[i][j] = 0;
P[i][i] = 1;
}
for(int i = 1; i <= k; i <<= 1){
if(i & k) P = mul(P, X);
X = mul(X, X);
}
return P;
}
signed main(){
int t, n;
dp[0][0] = dp[2][0] = 1;
T[0][0] = T[0][1] = T[0][2] = T[1][0] = T[2][2] = 1;
cin >> t;
while(t--){
cin >> n;
cout << mul(exp(T, n - 1), dp)[0][0] << "\n";
}
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
#define matrix array<array<int, 54>, 54>
using namespace std;
const int mod = 1e9 + 7;
int n;
matrix mul(matrix A, matrix B){
matrix C;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
C[i][j] = 0;
for(int k = 1; k <= n; k++){
C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
}
}
}
return C;
}
matrix exp(matrix X, int k){
matrix P;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++) P[i][j] = 0;
P[i][i] = 1;
}
for(int i = 1; i <= k; i <<= 1){
if(i & k) P = mul(P, X);
X = mul(X, X);
}
return P;
}
signed main(){
int k, sum = 0;
matrix G;
cin >> n >> k;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> G[i][j];
}
}
G = exp(G, k);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
sum = (sum + G[i][j]) % mod;
}
}
cout << sum << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
#define matrix array<array<int, 101>, 101>
using namespace std;
const int mod = 1e9 + 7;
matrix G;
matrix mul(matrix A, matrix B, int n){
matrix C;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
C[i][j] = 0;
for(int k = 1; k <= n; k++){
C[i][j] += (A[i][k] * B[k][j]) % mod;
}
C[i][j] %= mod;
}
}
return C;
}
int walk(int n, int k){
matrix W;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
W[i][j] = 0;
}
W[i][i] = 1;
}
for(int i = 1; i <= k; i <<= 1){
if(i & k) W = mul(G, W, n);
G = mul(G, G, n);
}
return W[n][1];
}
signed main(){
int n, m, k, a, b;
cin >> n >> m >> k;
while(m--){
cin >> a >> b;
G[b][a]++;
}
cout << walk(n, k);
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
#define matrix array<array<int, 101>, 101>
using namespace std;
matrix G, W;
int min(int a, int b){
if(a < 0) return b;
if(b < 0) return a;
return a < b? a : b;
}
matrix mul(matrix A, matrix B, int n){
matrix C;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
C[i][j] = -1;
for(int k = 1; k <= n; k++){
if(A[i][k] < 0 || B[k][j] < 0) continue;
C[i][j] = min(C[i][j], A[i][k] + B[k][j]);
}
}
}
return C;
}
int walk(int n, int k){
k--;
for(int i = 1; i <= k; i <<= 1){
if(i & k) W = mul(G, W, n);
G = mul(G, G, n);
}
return W[n][1];
}
signed main(){
int n, m, k, a, b, c;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
G[i][j] = -1;
W[i][j] = -1;
}
}
while(m--){
cin >> a >> b >> c;
G[b][a] = min(G[b][a], c);
W[b][a] = min(W[b][a], c);
}
cout << walk(n, k);
return 0;
}
扣
#include <bits/stdc++.h>
#define int unsigned
#define matrix array<array<int, 2>, 2>
using namespace std;
matrix T, dp;
matrix mul(matrix A, matrix B){
matrix C;
for(int i : {0, 1}){
for(int j : {0, 1}){
C[i][j] = 0;
for(int k : {0, 1}){
C[i][j] += A[i][k] * B[k][j];
}
}
}
return C;
}
matrix exp(matrix X, int k){
matrix P;
for(int i : {0, 1}){
for(int j : {0, 1}) P[i][j] = 0;
P[i][i] = 1;
}
for(int i = 1; i <= k; i <<= 1){
if(i & k) P = mul(P, X);
X = mul(X, X);
}
return P;
}
signed main(){
int a, b, x, y;
signed n;
while(cin >> n){
if(n < 0) break;
cin >> a >> b >> x >> y;
T[0][0] = y, T[0][1] = x;
T[1][0] = 1, T[1][1] = 0;
dp[0][0] = b, dp[1][0] = a;
cout << mul(exp(T, n), dp)[1][0] << "\n";
}
return 0;
}
單調隊列優化
單調隊列
作法
找到右邊第一個大於等於自己的
暴力作法
往右邊掃直到遇到比自己大的
(不知道為什麼聽起來很像廢話)
一些性質
右邊比自己小的都不重要
比右邊東西小的也對自己不重要
作法
搞一個 stack,從右到左丟每一隻牛的高度和位置進去。當top高度小於自己時,pop。答案就是無法被pop掉的位置和自己位置的距離
複雜度
\(O(N)\)
扣
#include <bits/stdc++.h>
using namespace std;
array<int, 1000004> H, see;
stack<pair<int, int>> S;
signed main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> H[i];
S.push({2147483647, n});
for(int i = n; i; i--){
while(1){
auto [h, p] = S.top();
if(H[i] > h) S.pop();
else{
see[i] = p - i;
break;
}
}
S.push({H[i], i});
}
for(int i = 1; i <= n; i++) cout << see[i] << "\n";
return 0;
}
拿單調隊列砸DP
狀態
\(dp_{i, j} = \) 第 \(i\) 種物品,花了 \(j\) 元,最大價值
轉移
\(dp_{i, j} = max_{k = j - W_i \times C_i}^j(dp_{i - 1, k} + \frac{j - k}{W_i} \times M_i)\)
優化
取max可以用單調隊列耶
優化
維護一個deque,把過期的東西從前面丟出去,新東西從後面進去,順便丟掉一些不會用到的狀態。
複雜度
Before : \(O(NTC)\)
After : \(O(NT)\)
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
int l, r;
array<int, 100004> W, M, C;
array<int, 1000004> Q;
array<array<int, 1000004>, 2> dp;
int DP(int n, int t){
int ans = 0;
for(int i = 1, p = 1; i <= n; i++, p ^= 1){
for(int j = 0; j < W[i]; j++){
l = 1, r = 0;
for(int k = j; k <= t; k += W[i]){
if(r >= l && Q[l] + W[i] * C[i] < k) l++;
while(r >= l && dp[!p][Q[r]] + (k - Q[r]) / W[i] * M[i] <= dp[!p][k]) r--;
Q[++r] = k;
dp[p][k] = dp[!p][Q[l]] + (k - Q[l]) / W[i] * M[i];
ans = max(ans, dp[p][k]);
}
}
}
return ans;
}
signed main(){
int n, t;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> W[i] >> M[i] >> C[i];
}
cin >> t;
cout << DP(n, t) << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 100004> H, W, Q, dp;
multiset<int> S;
int DP(int n, int k){
int l = 1, r = 0, len = 0;
Q[++r] = 0;
for(int i = 1; i <= n; i++){
len += W[i];
while(r > l && len > k){
S.erase(dp[Q[l]] + H[Q[l + 1]]);
len -= W[++Q[l]];
if(Q[l] == Q[l + 1]) l++;
else if(len <= k) S.insert(dp[Q[l]] + H[Q[l + 1]]);
}
while(r > l && H[i] >= H[Q[r]]){
S.erase(dp[Q[r - 1]] + H[Q[r]]);
r--;
}
S.insert(dp[Q[r]] + H[i]);
Q[++r] = i;
dp[i] = *S.begin();
}
return dp[n];
}
signed main(){
int n, l;
cin >> n >> l;
for(int i = 1; i <= n; i++){
cin >> H[i] >> W[i];
}
cout << DP(n, l) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 200004> K;
stack<pair<int, int>> F;
int run(int n){
int ans = 0, p;
for(int i = 1; i <= n; i++){
p = i;
while(!F.empty()){
auto [l, h] = F.top();
if(K[i] < h){
ans = max(ans, h * (i - l));
F.pop();
p = l;
}
else break;
}
F.push({p, K[i]});
}
while(!F.empty()){
auto [l, h] = F.top();
F.pop();
ans = max(ans, h * (n - l + 1));
}
return ans;
}
signed main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> K[i];
cout << run(n) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define ff first
#define ss second
using namespace std;
array<int, 1004> H;
array<array<char, 1004>, 1004> G;
stack<pair<int, int>> S;
int run(int n, int m){
int ans = 0, now;
for(int j = 1; j <= n; j++){
S.push({0, 0});
for(int i = 1; i <= m; i++){
now = i;
if(G[j][i] == '*') H[i] = 0;
else H[i]++;
while(!S.empty() && H[i] <= S.top().ff){
auto [h, p] = S.top();
S.pop();
ans = max(ans, (i - p) * h);
now = p;
}
S.push({H[i], now});
}
while(!S.empty()){
auto [h, p] = S.top();
S.pop();
ans = max(ans, (m - p + 1) * h);
}
}
return ans;
}
signed main(){
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> G[i][j];
}
}
cout << run(n, m) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 1004> H;
array<array<char, 1004>, 1004> G;
array<array<int, 1004>, 1004> cnt;
void run(int n, int m){
int now;
for(int i = 1; i <= n; i++){
stack<pair<int, int>> S;
S.push({0, 0});
for(int j = 1; j <= m; j++){
now = j;
if(G[i][j] == '*') H[j] = 0;
else H[j]++;
while(!S.empty()){
auto [h, p] = S.top();
if(H[j] < h){
cnt[h][j - p]++;
S.pop();
now = p;
auto [h2, p2] = S.top();
cnt[max(h2, H[j])][j - p]--;
}else{
break;
}
}
S.push({H[j], now});
}
}
for(int i = n; i; i--){
for(int j = m; j; j--){
cnt[i][j] += cnt[i][j + 1];
}
}
for(int i = n; i; i--){
for(int j = m; j; j--){
cnt[i][j] += cnt[i + 1][j] + cnt[i][j + 1] - cnt[i + 1][j + 1];
}
}
}
signed main(){
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> G[i][j];
}
G[i][m + 1] = '*';
}
run(n, m + 1);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cout << cnt[i][j] << " ";
}
cout << "\n";
}
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
int l, r;
array<int, 104> H, S, K;
array<int, 100004> Q;
array<array<int, 100004>, 2> dp;
int DP(int n, int x){
int p = 0, ans = 0;
for(int i = 1; i <= n; i++){
p ^= 1;
for(int j = 0; j < H[i]; j++){
l = 0, r = -1;
for(int k = j; k <= x; k += H[i]){
if(l <= r && Q[l] + K[i] * H[i] < k) l++;
while(l <= r && dp[p ^ 1][k] >= dp[p ^ 1][Q[r]] + (k - Q[r]) / H[i] * S[i]) r--;
Q[++r] = k;
dp[p][k] = dp[p ^ 1][Q[l]] + (k - Q[l]) / H[i] * S[i];
ans = max(ans, dp[p][k]);
}
}
}
return ans;
}
signed main(){
int n, x;
cin >> n >> x;
for(int i = 1; i <= n; i++) cin >> H[i];
for(int i = 1; i <= n; i++) cin >> S[i];
for(int i = 1; i <= n; i++) cin >> K[i];
cout << DP(n, x) << "\n";
return 0;
}
斜率優化
單調的斜率優化
狀態
\(dp_i = i\) 當最後一個的答案
轉移
\(dp_i = max_{j < i}(a(\sum\limits_{k = j + 1}^{i}{X_k})^2 + b(\sum\limits_{k = j + 1}^{i}{X_k}) + c + dp_j)\)
不是斜率優化的優化
\(dp_i = max_{j < i}(a(S_i - S_j)^2 + b(S_i - S_j) + c + dp_j)\)
\(S_i = \sum\limits_{j = 1}^{i}{X_j}\)
斜率優化
把轉移變成一條直線
\(y= ax + b\)
斜率優化
\(dp_i = (-2aS_j)(S_i) + (aS_j^2 - bS_j + dp_j) + aS_i^2 + bS_i + c\)
範測
維護線段
斜率單調 & 查詢單調
=> 開單調隊列存
複雜度
Before : \(O(N^2)\)
After : \(O(N)\)
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct line{
double a, b;
double operator*(double x){
return a * x + b;
}
pair<double, double> operator^(line f){
double x, y;
x = (f.b - b) / (a - f.a);
y = f * x;
return {x, y};
}
};
array<double, 1000004> X, dp;
array<line, 1000004> Q;
int DP(int n, double a, double b, double c){
int l = 1, r = 0;
line f;
Q[++r] = {0, 0};
for(int i = 1; i <= n; i++){
while(r > l && Q[l] * X[i] <= Q[l + 1] * X[i]) l++;
dp[i] = Q[l] * X[i] + a * X[i] * X[i] + b * X[i] + c;
f = {2 * -a * X[i], a * X[i] * X[i] - b * X[i] + dp[i]};
while(r > l){
auto [x, y] = f ^ Q[r - 1];
if(Q[r] * x <= y) r--;
else break;
}
Q[++r] = f;
}
return (int)dp[n];
}
signed main(){
int n;
double a, b, c;
cin >> n >> a >> b >> c;
for(int i = 1; i <= n; i++){
cin >> X[i];
X[i] += X[i - 1];
}
cout << DP(n, a, b, c) << "\n";
return 0;
}
不怎麼單調的斜率優化
斜率跟查詢都不單調耶
李超線段樹
每個節點存這個節點的區間中點最好的一條線
一棵裸的李超線段樹
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
struct line{
int a, b;
int operator*(int x){
return a * x + b;
}
};
array<line, C> seg;
void update(int p, int l, int r, line f){
if(l == r){
if(seg[p] * l < f * l) seg[p] = f;
return;
}
if(f.a < seg[p].a) swap(seg[p], f);
if(seg[p] * mid <= f * mid){
swap(f, seg[p]);
update(lc, l, mid, f);
}else update(rc, mid + 1, r, f);
}
int query(int p, int l, int r, int x){
if(l == r) return seg[p] * x;
if(x <= mid) return max(query(lc, l, mid, x), seg[p] * x);
else return max(query(rc, mid + 1, r, x), seg[p] * x);
}
複雜度
\(O(NlogC)\)
扣
#include <bits/stdc++.h>
#define int long long
#define mid ((l + r) >> 1)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
const int inf = 1 << 20;
struct line{
int a, b;
int operator*(int x){
return a * x + b;
}
};
array<int, 200004> F, S, dp;
array<line, (1 << 22) + 4> seg;
void build(){
for(line &f : seg) f = {inf, inf * inf};
}
void update(int p, int l, int r, line f){
if(l == r){
if(seg[p] * l > f * l) seg[p] = f;
return;
}
if(f.a > seg[p].a) swap(seg[p], f);
if(seg[p] * mid >= f * mid){
swap(f, seg[p]);
update(lc, l, mid, f);
}else update(rc, mid + 1, r, f);
}
int query(int p, int l, int r, int x){
if(l == r) return seg[p] * x;
if(x <= mid) return min(query(lc, l, mid, x), seg[p] * x);
else return min(query(rc, mid + 1, r, x), seg[p] * x);
}
int DP(int n, int x){
update(1, 1, inf, {x, 0});
for(int i = 1; i <= n; i++){
dp[i] = query(1, 1, inf, S[i]);
update(1, 1, inf, {F[i], dp[i]});
}
return dp[n];
}
signed main(){
int n, x;
cin >> n >> x;
build();
for(int i = 1; i <= n; i++) cin >> S[i];
for(int i = 1; i <= n; i++) cin >> F[i];
cout << DP(n, x) << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct line{
double a, b;
double operator*(double x){
return a * x + b;
}
pair<double, double> operator^(line f){
double x, y;
x = (b - f.b) / (f.a - a);
y = f * x;
return {x, y};
}
};
array<double, 200004> F, S, dp;
array<line, 200004> Q;
int DP(int n, double c){
int l = 1, r = 0;
line f;
Q[++r] = {c, 0};
for(int i = 1; i <= n; i++){
while(r > l && Q[l] * S[i] >= Q[l + 1] * S[i]) l++;
dp[i] = Q[l] * S[i];
f = {F[i], dp[i]};
while(r > l){
auto [x, y] = f ^ Q[r - 1];
if(Q[r] * x >= y) r--;
else break;
}
Q[++r] = f;
}
return (int)dp[n];
}
signed main(){
int n;
double x;
cin >> n >> x;
for(int i = 1; i <= n; i++) cin >> S[i];
for(int i = 1; i <= n; i++) cin >> F[i];
cout << DP(n, x) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define pb push_back
#define int long long
#define mid ((l + r) >> 1)
using namespace std;
struct ooo{
int d, p, r, g;
};
struct line{
int a, b;
int operator*(int x){
return a * x + b;
}
};
const int inf = 1ll << 30;
int k = 0;
array<int, 100004> dp;
vector<int> lc, rc;
vector<line> seg;
vector<ooo> Q;
bool cmp(ooo a, ooo b){
return a.d < b.d;
}
void add(){
lc.pb(0), rc.pb(0), seg.pb({0, 0});
}
void update(int p, int l, int r, line f){
if(l == r){
if(seg[p] * l < f * l) seg[p] = f;
return;
}
if(f.a < seg[p].a) swap(seg[p], f);
if(seg[p] * mid <= f * mid){
swap(f, seg[p]);
if(!lc[p]) lc[p] = ++k, add();
update(lc[p], l, mid, f);
}else{
if(!rc[p]) rc[p] = ++k, add();
update(rc[p], mid + 1, r, f);
}
}
int query(int p, int l, int r, int x){
if(l == r) return seg[p] * x;
if(x <= mid) return max(seg[p] * x, lc[p]? query(lc[p], l, mid, x) : 0);
else return max(seg[p] * x, rc[p]? query(rc[p], mid + 1, r, x) : 0);
}
int DP(int n){
int i = 1;
add();
for(auto [d, p, r, g] : Q){
dp[i] = query(0, 0, inf, d);
if(dp[i] >= p) update(0, 0, inf, {g, dp[i] - g * (d + 1) - p + r});
i++;
}
return dp[n];
}
signed main(){
int n, c, d, t, p, r, g;
cin >> n >> c >> d;
for(int i = 1; i <= n; i++){
cin >> t >> p >> r >> g;
Q.pb({t, p, r, g});
}
Q.pb({0, -c, 0, 0});
Q.pb({d + 1, 0, 0, 0});
sort(Q.begin(), Q.end(), cmp);
cout << DP(n + 2) << "\n";
return 0;
}
斜率優化 - Extreme
線段樹套李超線段樹
扣
#include <bits/stdc++.h>
#define int long long
#define mid ((l + r) >> 1)
#define lp (p << 1)
#define rp ((p << 1) | 1)
using namespace std;
const int inf = 1ll << 30;
struct line{
int a, b;
int operator*(int x){
return a * x + b;
}
bool operator==(line f){
if(a == f.a && b == f.b) return 1;
return 0;
}
};
struct superlee{
line f;
superlee *lc, *rc;
superlee(){f = {-inf, -inf * inf}, lc = rc = nullptr;}
void update(int l, int r, line g){
if(f == g) return;
if(l == r){
if(g * mid > f * mid) f = g;
return;
}
if(g.a < f.a) swap(f, g);
if(g * mid > f * mid){
swap(f, g);
if(!lc) lc = new superlee;
lc->update(l, mid, g);
}else{
if(!rc) rc = new superlee;
rc->update(mid + 1, r, g);
}
}
int query(int l, int r, int x){
if(l == r) return f * x;
if(x <= mid) return max(f * x, lc? lc->query(l, mid, x) : -inf * inf);
else return max(f * x, rc? rc->query(mid + 1, r, x) : -inf * inf);
}
};
array<int, 300004> A, B, S, H, dp;
array<superlee*, 1200004> seg;
void update(int p, int l, int r, int ql, int qr, line f){
if(ql > r || qr < l) return;
if(ql <= l && qr >= r){
seg[p]->update(-inf, inf, f);
return;
}
update(lp, l, mid, ql, qr, f);
update(rp, mid + 1, r, ql, qr, f);
}
int query(int p, int l, int r, int c, int x){
if(c > r || c < l) return -inf * inf;
if(l == r) return seg[p]->query(-inf, inf, x);
return max({seg[p]->query(-inf, inf, x), query(lp, l, mid, c, x), query(rp, mid + 1, r, c, x)});
}
int DP(int n, int k){
update(1, 0, n, 0, k, {0, 0});
for(int i = 1; i <= n; i++){
dp[i] = query(1, 0, n, i, S[i]) + H[i];
update(1, 0, n, i, i + k, {B[i] - i, dp[i] + (i - B[i]) * S[i] - H[i]});
}
return dp[n];
}
signed main(){
int n, k;
cin >> n >> k;
for(superlee *&s : seg) s = new superlee;
for(int i = 1; i <= n; i++){
cin >> A[i];
S[i] = S[i - 1] + A[i];
H[i] = H[i - 1] + i * A[i];
}
for(int i = 1; i <= n; i++) cin >> B[i];
cout << DP(n, k) << "\n";
return 0;
}
Aliens優化
先不管 K
狀態
\(dp_{i, t} = \) 現在時間 \(i\),手中有沒有商品的最多錢
轉移
\(dp_{i, 0} = max(dp_{i - 1, 0}, dp_{i - 1, 1} + P_i)\)
\(dp_{i, 1} = max(dp_{i - 1, 1}, dp_{i - 1, 0} - P_i)\)
優化
如何限制交易次數?
課稅!
優化
如果每次交易要課稅,那麼獲利在稅金以下的交易都不會進行
優化
二分搜稅金直到交易次數恰好為 K
複雜度
\(O(NlogC)\)
扣
#include <bits/stdc++.h>
#pragma optimize("Ofast")
#define int long long
using namespace std;
struct ooo{
int s, c;
ooo(){}
ooo(int ss, int cc): s(ss), c(cc){}
ooo operator+(ooo o){
return ooo(s + o.s, c + o.c);
}
};
array<int, 2000004> P;
array<array<ooo, 2>, 2000004> dp;
ooo max(ooo a, ooo b){
if(a.s != b.s) return a.s > b.s? a : b;
return a.c > b.c? a : b;
}
ooo DP(int n, int t){
dp[0][0] = {0, 0};
dp[0][1] = {-(1ll << 60), 0};
for(int i = 1; i <= n; i++){
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + ooo(P[i], 1));
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + ooo(-P[i] + t, 0));
}
return dp[n][0];
}
int BIS(int n, int k){
int l = -1e7, r = 0, mid;
while(l != r){
mid = (l + r) >> 1;
if(DP(n, mid).c < k) l = mid + 1;
else r = mid;
}
return DP(n, l).s - l * k;
}
signed main(){
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0);
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++){
cin >> P[i];
}
cout << BIS(n, k) << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct ufo{
int v, c;
ufo(){}
ufo(int val, int cnt): v(val), c(cnt){}
ufo operator+(ufo u){
return ufo(v + u.v, c + u.c);
}
bool operator>(ufo u){
return v >= u.v;
}
};
int l, r;
array<int, 300004> A, S;
array<ufo, 300004> dp, Q;
inline ufo turn(int j, int i, int p){
int k = (j + 1 + i) >> 1, cost = A[k] * (k - j) - (S[k] - S[j]) + (S[i] - S[k]) - A[k] * (i - k);
return dp[j] + ufo(cost + p, 1);
}
void BIS(int t, int k){
int mid, i, j;
while(r >= l){
i = Q[r].v, j = Q[r].c;
if(t < i && turn(j, i, 0) > turn(t, i, 0)) r--;
else break;
k = i;
}
while(i != k){
mid = (i + k) >> 1;
if(turn(j, mid, 0) > turn(t, mid, 0)) k = mid;
else i = mid + 1;
}
Q[++r] = ufo(i, t);
}
ufo DP(int n, int p){
l = 0, r = 0;
dp[0] = ufo(0, 0), Q[0] = ufo(0, 0);
for(int i = 1; i <= n; i++){
if(r > l && Q[l + 1].v <= i) l++;
dp[i] = turn(Q[l].c, i, p);
BIS(i, n + 1);
}
return dp[n];
}
int ALN(int n, int k){
int p = 0;
for(int i = 1ll << 60; i; i >>= 1){
if(DP(n, p + i).c >= k) p += i;
}
return DP(n, p).v - p * min(DP(n, p).c, k);
}
signed main(){
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0);
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> A[i];
sort(A.begin() + 1, A.begin() + n + 1);
for(int i = 1; i <= n; i++){
S[i] = A[i] + S[i - 1];
}
A[n + 1] = A[n] + 1, S[n + 1] = A[n + 1] + S[n];
cout << ALN(n, k) << "\n";
return 0;
}
扣
嗷嗷待補
分治優化
狀態
\(dp_{i, j} = \) 最後在 \(i\),設了 \(j\) 個郵局的答案
轉移
\(dp_{i, j} = max_{k < j}(dp_{i - 1, k} + cost(k + 1, j))\)
優化
如果 \(i \le j\),\(i\) 的轉移點 \(\le j\) 的轉移點
優化
對於 \((l, r)\) 找到 \(mid\) 的轉移點,就可以得到 \((l, mid)\) 和 \((mid + 1, r)\) 轉移點的範圍
複雜度
Before : \(O(N^2K)\)
After : \(O(NKlogN)\)
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 1 << 10> A, S;
array<array<int, 1 << 10>, 1 << 10> C, dp;
void build(int n){
int mid;
for(int k = 1; k <= n; k++){
for(int i = 1, j = k; j <= n; i++, j++){
mid = (i + j) >> 1;
C[i][j] += (mid - i + 1) * A[mid] - (S[mid] - S[i - 1]);
C[i][j] += (S[j] - S[mid]) - (j - mid) * A[mid];
}
}
}
void DC(int l, int r, int tl, int tr, int k){
int mid = (l + r) >> 1, t;
dp[mid][k] = 1ll << 60;
for(int i = tl; i <= min(mid - 1, tr); i++){
if(dp[mid][k] > dp[i][k - 1] + C[i + 1][mid]){
dp[mid][k] = dp[i][k - 1] + C[i + 1][mid];
t = i;
}
}
if(l == r) return;
DC(l, mid, tl, t, k);
DC(mid + 1, r, t, tr, k);
}
int DP(int n, int k){
for(int i = 1; i <= k; i++) DC(i, n, i - 1, n, i);
return dp[n][k];
}
signed main(){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++) dp[i][0] = 1ll << 60;
for(int i = 2; i <= n; i++) cin >> A[i];
sort(A.begin() + 1, A.begin() + n + 1);
for(int i = 1; i <= n; i++) S[i] = A[i] + S[i - 1];
build(n);
cout << DP(n, min(n, k)) << "\n";
return 0;
}
題目
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 3004> X;
array<array<int, 3004>, 3004> dp;
int cost(int l, int r){
return (X[r] - X[l]) * (X[r] - X[l]);
}
void div(int ql, int qr, int l, int r, int k){
int t, qm = (ql + qr) >> 1;
dp[k][qm] = 1e18;
for(int i = l; i < min(r + 1, qm); i++){
if(dp[k - 1][i] + cost(i, qm) < dp[k][qm]){
t = i;
dp[k][qm] = dp[k - 1][i] + cost(i, qm);
}
}
if(ql == qr) return;
div(ql, qm, l, t, k);
div(qm + 1, qr, t, r, k);
}
int DP(int n, int k){
for(int i = 1; i <= n; i++){
dp[1][i] = X[i] * X[i];
}
for(int i = 2; i <= k; i++){
div(i, n, i - 1, n, i);
}
return dp[k][n];
}
signed main(){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++){
cin >> X[i];
X[i] += X[i - 1];
}
cout << DP(n, k) << "\n";
return 0;
}
四邊形優化
區間低批
\(dp_{i, j} = min_{i \le k < j}(dp_{i, k} + dp_{k + 1, j} + f(i, j))\)
四邊形不等式
\(a \le b \le c \le d\)
\(f(b, c) \le f(a, d)\)
\(f(a, c) + f(b, d) \le f(a, d) + f(b, c)\)
性質
\(a \le b \le c \le d\)
\(dp_{a, c} + dp_{b, d} \le dp_{a, d} + dp_{b, c}\)
性質
\(l < r\)
\(t(l, r) = dp_{l, r}\) 最佳轉移點
\(t(l, r - 1) \le t(l, r) \le t(l + 1, r)\)
複雜度
\(t(l, r) = dp_{l, r}\) 最佳轉移點
\(t(l - 1, r - 1) \le t(l, r - 1) \le t(l, r) \le t(l + 1, r) \le t(l + 1, r + 1)\)
\(O(N^2)\)
另一種低批
\(dp_i = min_{j < i}(dp_j + f(j + 1, i))\)
凸性優化
\(T(i, j) = dp_j + f(j + 1, i), a \le b \le c \le d\\ if(T(b, c) \le T(a, c)) : T(b, d) \le T(a, d) \\ \to if(T(a, d) \le T(b, d) : T(a, c) \le T(b, c))\)
X | O | O | |||||
X | X | ||||||
X | X | X | O | O | |||
X | X | X | X | ||||
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
\(if(j 為 i 最佳轉移點) : \)
X | X | X | X | ||||
X | O | ||||||
X | X | X | X | X | |||
X | X | X | X | X | |||
X | X | X | X | X | |||
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
\(O為預期最佳轉移點\)
X | O | ||||||
X | X | O | |||||
X | X | X | O | O | |||
X | X | X | X | ||||
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
X | O | ||||||
X | X | O | |||||
X | X | X | |||||
X | X | X | X | O | O | ||
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
凹性優化
\(T(i, j) = dp_j + f(j + 1, i), a \le b \le c \le d\\ if(T(a,c) \le T(b, c)) : T(a, d) \le T(b, d) \\ \to if(T(b, d) \le T(a, d) : T(b, c) \le T(a, c))\)
X | O | O | |||||
X | X | ||||||
X | X | X | O | O | |||
X | X | X | X | ||||
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
\(if(j 為 i 最佳轉移點) : \)
X | X | X | X | X | |||
X | O | ||||||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X | |
X | X | X | X | X | X | X | X |
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
\(O為預期最佳轉移點\)
O | |||||||
X | O | ||||||
X | X | O | |||||
X | X | X | O | ||||
X | X | X | X | ||||
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
O | |||||||
X | |||||||
X | X | ||||||
X | X | X | |||||
X | X | X | X | O | O | O | |
X | X | X | X | X | |||
X | X | X | X | X | X | ||
X | X | X | X | X | X | X |
優化方法
最佳轉移點有單調性
二分搜從哪裡開始某個轉移點會比較好
\(O(NlogN)\)
題目
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 5004> X;
array<array<int, 5004>, 5004> dp, turn;
int DP(int n){
for(int i = 1; i <= n; i++){
dp[i][i] = 0;
turn[i][i] = i;
}
for(int k = 1; k < n; k++){
for(int i = 1, j = i + k; j <= n; i++, j++){
dp[i][j] = 1e18;
for(int t = turn[i][j - 1]; t <= turn[i + 1][j]; t++){
if(dp[i][t] + dp[t + 1][j] < dp[i][j]){
turn[i][j] = t;
dp[i][j] = dp[i][t] + dp[t + 1][j];
}
}
dp[i][j] += X[j] - X[i - 1];
}
}
return dp[1][n];
}
signed main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> X[i];
X[i] += X[i - 1];
}
cout << DP(n) << "\n";
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct dot{
int x, y;
bool operator<(dot d){
return x < d.x;
}
int operator*(dot d){
return (d.x - x) * (d.y - y);
}
};
array<dot, 100004> up, down;
int solve(int ul, int ur, int dl, int dr){
int dmid = (dl + dr) / 2, umid, ans = 0;
for(int i = ul; i <= ur; i++){
if(down[dmid] < up[i]){
if(down[dmid] * up[i] > ans){
ans = down[dmid] * up[i];
umid = i;
}
}
}
if(dl == dr) return ans;
return max({ans, solve(ul, umid, dl, dmid), solve(umid, ur, dmid + 1, dr)});
}
signed main(){
int n, m, x = 0, y = 0, z;
cin >> n;
for(int i = 0; i < n; i++){
cin >> z;
if(i & 1) y -= z;
else{
x += z;
up[i / 2] = {x, y};
}
}
x = y = 0;
cin >> m;
for(int i = 0; i < m; i++){
cin >> z;
if(i & 1) x += z;
else{
y -= z;
down[i / 2] = {x, y};
}
}
cout << solve(0, n / 2 - 1, 0, m / 2 - 1);
return 0;
}
扣
#include <bits/stdc++.h>
#define int long long
using namespace std;
array<int, 3004> C;
array<array<int, 3004>, 3004> dis, cst, turn, dp;
void DIS(int n){
for(int i = 1; i <= n; i++){
for(int j = i - 1; j > 0; j--){
dis[i][j] = (i - j) * C[j] + dis[i][j + 1];
}
for(int j = i + 1; j <= n; j++){
dis[i][j] = (j - i) * C[j] + dis[i][j - 1];
}
}
for(int i = 1; i <= n; i++){
cst[i][i] = 0;
turn[i][i] = i;
}
for(int k = 1; k < n; k++){
for(int i = 1, j = i + k; j <= n; i++, j++){
cst[i][j] = 1e18;
for(int t = turn[i][j - 1]; t <= turn[i + 1][j]; t++){
if(dis[t][i] + dis[t][j] < cst[i][j]){
cst[i][j] = dis[t][i] + dis[t][j];
turn[i][j] = t;
}
}
}
}
}
void div(int ql, int qr, int l, int r, int k){
int t, qm = (ql + qr) >> 1;
dp[k][qm] = 1e18;
for(int i = l; i < min(r + 1, qm); i++){
if(dp[k][qm] > dp[k - 1][i] + cst[i + 1][qm]){
dp[k][qm] = dp[k - 1][i] + cst[i + 1][qm];
t = i;
}
}
if(ql == qr) return;
div(ql, qm, l, t, k);
div(qm + 1, qr, t, r, k);
}
int DP(int n, int k){
for(int i = 1; i <= n; i++){
dp[1][i] = cst[1][i];
}
for(int i = 2; i <= k; i++){
div(i, n, i - 1, n, i);
}
return dp[k][n];
}
signed main(){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> C[i];
DIS(n);
cout << DP(n, k) << "\n";
return 0;
}
低批
By thanksone
低批
- 1,112