序列分割题解

序列分割题解

看上去是区间\(DP\)题,实则不然:
毕竟区间\(DP\) \(O(n^3)\)的复杂度难以优化,
那么,我们是不是可以想到,
每一块的贡献是否与切割顺序无关?
证明一下:
假设区间\([l_{1},r_{3}]\),被分割为\([l_{1},r_{1}],[l_{2},r_{2}],[l_{3},r_{3}]\)
我们算一算\([l_{1},r_{1}]\)的贡献,
先分割\(r_{1},l_{2}\),再分割\(r_{2},l_{3}\),只有一次贡献,为:\((sum[r_{1}]-sum[l_{1}-1])*(sum[r_{3}]-sum[r_{1}])\)
先分割\(r_{2},l_{3}\),再分割\(r_{1},l_{2}\),有两次贡献,为:\((sum[r_{1}]-sum[l_{1}-1])*[(sum[r_{3}]-sum[r_{2}])+(sum[r_{2}]-sum[r_{1}])]=(sum[r_{1}]-sum[l_{1}-1])*(sum[r_{3}]-sum[r_{1}])\)
也可以感性理解一下,
每种情况都可视为将\([l,r]\)后面的\([r+1,n]\)从右至左分成k段,其贡献为:\((sum[r]-sum[l-1])*\sum_{i=1}^{k}(sum[r_{i}]-sum[r_{i-1}])=(sum[r]-sum[l-1])*(sum[n]-sum[r])\)
我们就可以用费用前移算出贡献了。
所以,暴力线性DP方程为:\(f[i][j]=f[i-1][k]+(sum[j]-sum[k])*(sum[n]-sum[j])\)
拆括号,移项:\(f[i-1][k]=sum[k]*(sum[n]-sum[j])+f[i][j]-(sum[n]-sum[j])*sum[j]\)
\(\bullet y=f[i-1][k]\)
\(\bullet k=sum[n]-sum[j]\)
\(\bullet x=sum[k]\)
\(\bullet b=f[i][j]-(sum[n]-sum[j])*sum[j]\)
由于k递减但大于0,x递增,求最大值,我们可以维护这样的凸壳:
序列分割题解_第1张图片
接下来就是斜率优化的板子了。
代码:

#include
#define ll long long
using namespace std;
const int N=100006,K=206;
int o=1,n,k,head=1,tail=1,pre[K][N];
ll t,f[K][N],sum[N];
struct point{ll x,y; int p;}tmp,q[N];
bool check(point u,point v,int z){return v.y-u.y>=(sum[n]-sum[z])*(v.x-u.x);}
bool check2(point u,point v,point z){return (v.y-u.y)*(z.x-v.x)<=(z.y-v.y)*(v.x-u.x);}
inline int read(){
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
   return F*T;
}
void dfs(int u,int v){
     if(!u) return;
     dfs(u-1,pre[u][v]);
     if(v!=n) printf("%d ",v);
}
int main(){
   n=read(),k=read(),++k;
   for(int i=1;i<=n;++i) t=read(),sum[i]=sum[i-1]+t;
   for(int j=1;j<=k;++j){
       q[1].x=q[1].y=q[1].p=0,head=tail=1;
       for(int i=1;i<=n;++i){
           while(head

你可能感兴趣的:(序列分割题解)