题解 CF573E 【Bear and Bowling】

mrsrz

2019-03-12 15:28:52

Solution

有一种神奇的$O(n)$贪心做法,在CF上能过,然鹅是错的,随便卡卡就WA了。 --- 以下算法比标算的$O(n\sqrt n\log n)$要优,证明来自YuHaoxiang。 有一个$O(n^2)$的dp:设$f_{i,j}$表示前$i$个数选$j$个数的最大值。 $f_{i,j}=\max(f_{i-1,j},f_{i-1,j-1}+j\times a_i)$ 有一个结论:对于$a_i$,一定存在一个$1\leqslant k\leqslant i$使得任意$0\leqslant j < k$,有$f_{i,j}=f_{i-1,j}$;任意$k\leqslant j\leqslant i$,有$f_{i,j}=f_{i-1,j-1}+j\times a_i$。 即存在一个分界点,使得左边的都不取$a_i$,右边的都取了$a_i$。 下面来证明这个结论。 当$i=0,1$时显然成立。 设$F_{i,j}$表示前$i$个数选$j$个数,最大方案选的下标集合,$V(F_{i,j})$表示取这个下标集合的值。 假设存在一个$j$,使得$i\in F_{i,j}$且$i\notin F_{i,j+1}$。 设$F_{i-1,j}=F_{i-1,j-1}\cup\{x\}$,$F_{i-1,j+1}=F_{i-1,j}\cup\{z\}=F_{i-1,j-1}\cup\{x,z\}$,$F_{i,j}=F_{i-1,j-1}\cup\{i\}$。 由假设得$F_{i,j+1}=F_{i-1,j+1}=F_{i-1,j}\cup\{z\}$。 令每种方案的值各不相同(相同可以通过扰动来避免),则$f_{i-1,j}< f_{i,j}$。 由此可得$V(F_{i-1,j-1}\cup\{z\})< V(F_{i-1,j-1}\cup\{x\})$。 1. 若$x<z$,则$V(F_{i-1,j-1}\cup\{z\})< V(F_{i-1,j-1}\cup\{x\})< V(F_{i-1,j-1}\cup\{i\})$。由于$x<z$且$x<i$,所以$V(F_{i-1,j-1}\cup\{x,z\})<V(F_{i-1,j-1}\cup\{x,i\})$。 2. 若$x>z$,则$V(F_{i-1,j-1}\cup\{x\}))< V(F_{i-1,j-1}\cup\{i\})$。由于$z<x$且$z<i$,所以$V(F_{i-1,j-1}\cup\{z,x\})< V(F_{i-1,j-1}\cup\{z,i\})$。又有$V(F_{i-1,j-1}\cup\{z\})< V(F_{i-1,j-1}\cup\{x\})$,由于$z< i$且$x< i$,所以$V(F_{i-1,j-1}\cup\{z,i\})< V(F_{i-1,j-1}\cup\{x,i\})$。所以$V(F_{i-1,j-1}\cup\{z,x\})< V(F_{i-1,j-1}\cup\{x,i\})$。 综上,$V(F_{i-1,j-1}\cup\{z,x\})< V(F_{i-1,j-1}\cup\{x,i\})$。 所以$f_{i-1,j+1}< f_{i-1,j}+(j+1)\times a_i$,即$i\in F_{i,j+1}$,与假设矛盾。 由数学归纳法可知结论成立。 --- $f$的第一维可以去掉。 然后我们就可以二分出开始转移的位置,更新后面的值即可。 这样的时间复杂度还是$O(n^2)$的。 观察到每次转移,相当于在某处插入一个新值,然后给后面一部分加上一个等差数列。 所以用平衡树来实现即可。 时间复杂度$O(n\log^2 n)$。 ## Code: ```cpp #include<cstdio> #include<algorithm> typedef long long LL; const int N=1e5+5; int fa[N],ch[N][2],sz[N];LL A[N],B[N],val[N]; int n,a,rt,cnt; inline int ckr(int x,const int p=1){return ch[fa[x]][p]==x;} inline void update(int x){const int L=ch[x][0],R=ch[x][1];sz[x]=sz[L]+sz[R]+1;} inline void modify(int x,LL a,LL b){val[x]+=a*(sz[ch[x][0]]+1)+b,A[x]+=a,B[x]+=b;} inline void pushdown(int x){ if(A[x]||B[x]){ if(ch[x][0])modify(ch[x][0],A[x],B[x]); if(ch[x][1])modify(ch[x][1],A[x],B[x]+A[x]*(sz[ch[x][0]]+1)); A[x]=B[x]=0; } } inline void rotate(int x){ int y=fa[x],z=fa[y],k=ckr(x); if(z)ch[z][ckr(y)]=x; fa[x]=z,fa[y]=x,fa[ch[x][!k]]=y,ch[y][k]=ch[x][!k],ch[x][!k]=y,update(y); } inline void splay(int x){ static int sta[N],top;sta[top=1]=x; for(int y=x;fa[y];sta[++top]=y=fa[y]); while(top)pushdown(sta[top--]); for(;fa[x];rotate(x))if(fa[fa[x]])rotate((ckr(x)^ckr(fa[x]))?x:fa[x]);update(rt=x); } inline LL getv(int k){ int x=rt; while(1){ if(sz[ch[x][0]]+1==k){ splay(x);return val[x]; } if(sz[ch[x][0]]>=k)x=ch[x][0];else k-=sz[ch[x][0]]+1,x=ch[x][1]; } } LL chkmax(int x){ if(!x)return -1e18; pushdown(x); return std::max(val[x],std::max(chkmax(ch[x][0]),chkmax(ch[x][1]))); } int main(){ scanf("%d",&n); sz[1]=rt=cnt=1; for(int i=1,a;i<=n;++i){ scanf("%d",&a); int l=0,r=i-2,ans=i-1; while(l<=r){ const int mid=l+r>>1; if(getv(mid+1)+(mid+1LL)*a>getv(mid+2))r=(ans=mid)-1;else l=mid+1; } getv(ans+1); fa[++cnt]=rt,fa[ch[rt][1]]=cnt,ch[cnt][1]=ch[rt][1],ch[rt][1]=cnt,val[cnt]=val[rt]; modify(cnt,a,(LL)a*ans); } printf("%lld\n",chkmax(rt)); return 0; } ```