cf1420 C2. Pokémon Army (hard version)
题意:
给定数组,你可以任选一个子序列 \(a_{b_1},\cdots ,a_{b_k}\),求它的交错和 $sum=a_{b_1}-a_{b_2}+a_{b_3}\cdots $
(注意不能改变顺序)
每次询问交换两个数,输出交换前后最大的子序列交错和
\(n,q\le 3e5, 1\le a_i\le n\)
思路:
法一:动态dp(线段树加速矩阵dp)
很容易写出dp:
\(f(i,1/0)\) 表示选的最后一个数的符号是正/负的,
初值 \(f(0,0)=f(0,1)=0\)
转移方程:
\(f(i,0)=\max \{f(i-1,0),f(i-1,1)-a_i \}\)
\(f(i,1)=\max \{f(i-1,1),f(i-1,0)+a_i \}\)
把乘法改成加法,加法改成 \(\max\),写出矩阵形式:
\[\begin{bmatrix}
0 & -a_i \\
a_i & 0
\end{bmatrix}
\begin{bmatrix}
f(i-1,0)\\f(i-1,1)
\end{bmatrix}
=
\begin{bmatrix}
f(i,0)\\f(i,1)
\end{bmatrix}
\]
用线段树维护一下(单点修改、查询全区间)
因为初始矩阵是全0的,我又把列向量看成2×2矩阵,所以矩阵是对称的,查询答案随便取矩阵中的两个不是对角的元素取 \(\max\) 即可
甚至懒得写build,建树的时候直接更新 n 次
const signed N = 3 + 3e5;
int n, q, a[N];
#define Matrix array<array<ll,2>,2>
Matrix mul(Matrix &a, Matrix &b) {
Matrix res = {0,-LLINF,-LLINF,0}; //幺元
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
for(int k = 0; k < 2; k++)
res[i][j] = max(res[i][j], a[i][k] + b[k][j]);
return res;
}
Matrix tr[N*4];
void upd(int u, int l, int r, int p, int x) {
if(l == r) return tr[u] = {0,-x,x,0}, void();
if(p <= mid) upd(ls, p, x); else upd(rs, p, x);
tr[u] = mul(tr[lson], tr[rson]); //pushup
}
ll ans() {
return max(tr[1][0][0], tr[1][1][0]);
}
void sol() {
cin >> n >> q;
for(int i = 1; i <= n; i++)
cin >> a[i], upd(1, 1, n, i, a[i]);
cout << ans() << endl;
while(q--) {
int x, y; cin >> x >> y;
swap(a[x], a[y]);
upd(1, 1, n, x, a[x]), upd(1, 1, n, y, a[y]);
cout << ans() << endl;
}
}
法二:
实际上要选所有波峰和波谷,当然开头和结尾会稍有不同
答案就是 \(\sum\limits_i \max \{ 0,a_i-a_{i-1} \}\)
每次询问只会改变几个点。注意交换相邻点的时候的操作有所不同
int n, q, a[N];
ll d(int i) { return max(0, a[i]-a[i-1]); }
void sol() {
cin >> n >> q;
a[0] = a[n+1] = 0; ll ans = 0;
for(int i = 1; i <= n; i++)
cin >> a[i], ans += d(i);
cout << ans << endl;
while(q--) {
int x, y; cin >> x >> y;
if(x + 1 == y) {
ans -= d(x) + d(y+1) + d(y);
swap(a[x], a[y]);
ans += d(x) + d(y+1) + d(y);
}
else {
ans -= d(x) + d(x+1) + d(y) + d(y+1);
swap(a[x], a[y]);
ans += d(x) + d(x+1) + d(y) + d(y+1);
}
cout << ans << endl;
}
}

浙公网安备 33010602011771号