CF1420C2. Pokémon Army (hard version)(线段树维护矩阵乘法)
题意:
这是问题的硬性版本。两个版本之间的区别在于,简单版本没有交换操作。只有解决了所有版本的问题,您才可以进行破解。
皮卡丘是生活在野生皮卡丘群中的可爱友好的神奇宝贝。
但是最近众所周知,臭名昭著的R队想偷走所有这些神奇宝贝!神奇宝贝训练师安德鲁决定帮助皮卡丘组建一支反抗神奇宝贝的军队。
首先,安德鲁算出了所有的神奇宝贝-皮卡丘正好是n个。第i个神奇宝贝的力量等于ai,并且所有这些数字都是不同的。
作为一支军队,安德鲁可以选择任何非空的神奇宝贝子序列。换句话说,安德鲁从k个索引中选择了一些数组b,使得1≤b1<b2 <⋯<bk≤n,他的军队将由力量为ab1,ab2,…,abk的神奇宝贝组成。
军队的力量等于子序列各要素的交替总和。即ab1-ab2 + ab3-ab4 +...。
安德鲁正在尝试神奇宝贝秩序。他执行q次运算。在第i次行动中,安德鲁交换了第口袋妖怪和第ri个口袋妖怪。
安德鲁想知道他在最初的神奇宝贝放置后可以达到的最大步兵力量。他还需要知道每次手术后的最大力量。
帮助安德鲁和神奇宝贝或R团队实现他们的棘手计划!
题解:
我C1是用DP过的,C2是在C1的基础上带修改,这里是C1的转移:
#include<bits/stdc++.h> using namespace std; const int maxn=3e5+100; typedef long long ll; ll dp[maxn][2]; int n,q,t; int a[maxn]; int main () { scanf("%d",&t); while (t--) { scanf("%d%d",&n,&q); for (int i=1;i<=n;i++) scanf("%d",a+i); for (int i=0;i<=n;i++) dp[i][1]=dp[i][0]=0; for (int i=1;i<=n;i++) { dp[i][1]=max(dp[i][1],max(dp[i-1][0]+a[i],dp[i-1][1])); dp[i][0]=max(dp[i][0],max(dp[i-1][1]-a[i],dp[i-1][0])); } printf("%lld\n",max(dp[n][0],dp[n][1])); } }
C2带转移的话就根据这个方程造一个2*2的矩阵,观察转移式子不难推出右上角是-a[i],左下角是a[i]。但比赛的时候死活过不去样例,看来红名大神们的代码才发现,用线段树维护带max的矩阵乘法时要加一步很重要的松弛操作。具体看代码。
#include<bits/stdc++.h> using namespace std; const int maxn=3e5+100; typedef long long ll; int n,q,t; int a[maxn]; struct matrix { ll m[3][3]; }; void mul (matrix &ans,matrix a,matrix b) { for (int i=1;i<=2;i++) for (int j=1;j<=2;j++) ans.m[i][j]=max(a.m[i][j],b.m[i][j]);//很重要的松弛操作!!! for (int k=1;k<=2;k++) for (int i=1;i<=2;i++) for (int j=1;j<=2;j++) ans.m[i][j]=max(ans.m[i][j],a.m[i][k]+b.m[k][j]); } struct node { int l,r; matrix sum; ll get () { ll ans=0; for (int i=1;i<=2;i++) for (int j=1;j<=2;j++) ans=max(ans,sum.m[i][j]); return ans; } }segTree[maxn<<2]; void build (int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; if (l==r) { segTree[i].sum.m[1][2]=-a[l]; segTree[i].sum.m[2][1]=a[l]; segTree[i].sum.m[1][1]=0; segTree[i].sum.m[2][2]=0; return; } int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); mul (segTree[i].sum,segTree[i<<1].sum,segTree[i<<1|1].sum); } void up (int i,int p,int v) { if (segTree[i].l==p&&segTree[i].r==p) { segTree[i].sum.m[1][2]=-v; segTree[i].sum.m[2][1]=v; segTree[i].sum.m[1][1]=0; segTree[i].sum.m[2][2]=0; return; } int mid=(segTree[i].l+segTree[i].r)>>1; if (p<=mid) up(i<<1,p,v); if (p>mid) up(i<<1|1,p,v); mul (segTree[i].sum,segTree[i<<1].sum,segTree[i<<1|1].sum); } int main () { scanf("%d",&t); while (t--) { scanf("%d%d",&n,&q); for (int i=1;i<=n;i++) scanf("%d",a+i); build(1,1,n); printf("%lld\n",segTree[1].get()); while (q--) { int l,r; scanf("%d%d",&l,&r); up(1,l,a[r]);up(1,r,a[l]);swap(a[l],a[r]); printf("%lld\n",segTree[1].get()); //up(1,l,a[l]);up(1,r,a[r]); } } }