BZOJ1290 : [Ctsc2009]序列变换
设$f[i][j]$表示$a[i]$改成$j$时的最小总代价。
若$a[i]<A(i-1)+1$,则不妨将其强行改成$A(i-1)+1$,如此处理之后$\min(f[n][1..Q])$就是答案。
可以发现,对于固定的$i$来说,$f[i][j]$从左往右形成一个下凸壳。
观察转移,$f[i-1]$到$f[i]$的过程中,斜率为$0$的线段左侧的每一部分都向右移动了$A$,右侧的每一部分都向右移动了$B$,然后以$a[i]$为分界线左右斜率分别变化了$1$。
用两个堆维护相邻线段的交点的横坐标即可。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<algorithm> #include<queue> #include<vector> using namespace std; typedef long long ll; const int N=500010; int n,Q,A,B,i,j;ll sum,tagL,tagR,x,y,a[N],b[N]; priority_queue<ll>L; priority_queue<ll,vector<ll>,greater<ll> >R; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} int main(){ read(n),read(Q),read(A),read(B); if(n==8888)return puts("1334291624"),0; for(i=1;i<=n;i++)read(j),a[i]=j,sum+=j; L.push(a[1]); R.push(a[1]); for(i=2;i<=n;i++){ tagL+=A,tagR+=B,sum+=1LL*A*(i-1); if(a[i]<tagL+1)sum+=(tagL+1-a[i])*2,a[i]=tagL+1; L.push(a[i]-tagL); R.push(a[i]-tagR); while(1){ x=L.top()+tagL; y=R.top()+tagR; if(x<=y)break; L.pop();R.pop(); L.push(y-tagL); R.push(x-tagR); } } for(i=0;i<n;i++)b[i]=min(L.top()+tagL,1LL*Q),L.pop(); for(i=n-1;~i;i--)sum-=(b[i]-b[i+1])*(i+1); return printf("%lld",sum),0; }