4 条题解

  • 4
    @ 2025-6-27 17:02:25

    f[i]f[i] 表示前i i 个数划分完后的最小代价

    s[i]s[i] 表示前i i 个数的和

    f[i]=min(f[j]+(s[i]s[j])2+C)f[i]=min(f[j]+(s[i]-s[j])^2+C)

    这个是典型的一维动态规划,而且它的w(i,j)w(i,j)是关于s[i]s[j]s[i]*s[j]的,因此我们想到要用斜率优化

    w(i,j)=(s[i]s[j])2+Cw(i,j) = (s[i]-s[j])^2+C

    然后我们容易证明

    w(i,j)+w(i+1,j+1)<=w(i+1,j)+w(i,j+1)w(i,j)+w(i+1,j+1) <= w(i+1,j) + w(i,j+1)

    则它满足四边形不等式

    因此这个递推式子有决策单调性

    然后我们化简一下式子

    f[i]s[i]s[i]c=f[j]2s[j]s[i]+s[j]s[j]f[i]-s[i]*s[i]-c=f[j]-2*s[j]*s[i]+s[j]*s[j]

    我们如果要用斜率优化,我们要满足kk为一个关于ii的东西且它得和一个关于jj的东西相乘,xx为一个关于jj的东西且它得和一个关于ii的东西相乘,bb为一个关于ii的东西,yy为一个关于jj的东西

    那么我们可以得出

    x=s[j]x = s[j] y=f[j]+s[j]s[j]y = f[j] + s[j]*s[j] k=2s[i]k = 2*s[i] b=f[i]s[i]s[i]cb = f[i]-s[i]*s[i]-c

    这样我们的问题就变为如何让这个一次函数的截距最小

    我们设k<j<ik < j < ijjkk

    即$f[j]-2*s[j]*s[i]+s[j]*s[j]<f[k]-2*s[k]*s[i]+s[k]*s[k]$

    化简可得Y(j)Y(k)/X(j)X(k)<=2s[i]Y(j)-Y(k)/X(j)-X(k)<=2*s[i]

    那么只要满足这个式子就可以保证jjkk

    假设有三个点P(j1)P(j1) P(j2)P(j2) P(j3)P(j3)

    k1k1P(j1)P(j1) P(j2)P(j2)的斜率

    k2k2P(j3)P(j3) P(j2)P(j2)的斜率

    假设k2<k1k2 < k1 k0k02s[i]2*s[i]

    k0<k2<k1k0 < k2 < k1 j1j1优于j2j2优于j3j3 k2<=k0<k1k2 <= k0 < k1 j1j1,j3j3优于j2j2 k2<k1<=k0k2 < k1 <= k0 j3j3优于j2j2优于j1j1

    可以发现这三个情况j2j2都是劣的

    所以我们可以把它扔掉

    所以我们要维护的点是在一个下凸包

    然后他的斜率是单增的

    然后我们最优的点就是最小的那个满足k<=2s[i]k<=2s[i],然后他又有决策单调性

    所以我们可以用一个单调队列,队头用k<=2s[i]k<=2s[i]去判,如果不行就扔,队尾维护一个下凸包就好

    然后这个题就做完了

    #include <bits/stdc++.h>
    using namespace std;
    
    const long long N = 5e5 + 10;
    
    long long n, c, a[N], s[N], f[N], st[N], h, t;
    
    void read(){
    	for(long long i = 1;i <= n; i++){
    		cin >> a[i];
    		s[i] = s[i-1] + a[i]; 
    	}
    }
    
    double X(long long i){
    	return s[i];
    }
    
    double Y(long long i){
    	return f[i] + s[i] * s[i];
    }
    
    double k(long long a,long long b){
    	if(X(a)-X(b) == 0) return 1e18;
    	return (Y(a)-Y(b))/(X(a)-X(b));
    }
    
    void compute(){
    	memset(f,0,sizeof f);
    	memset(st,0,sizeof st);
    	h = t = 0;
    	st[t++] = 0;
    	for(long long i = 1;i <= n; i++){
    		while(t - h > 1 && k(st[h],st[h+1]) <= s[i] * 2) h++;
    		f[i] = f[st[h]] + (s[st[h]] - s[i]) * (s[st[h]] - s[i]) + c;
    		while(t - h > 1 && k(st[t-1],st[t-2]) >= k(st[t-1],i)) t--;
    		st[t++] = i;
    	}
    	cout << f[n] << '\n';
    }
    
    int main(){
    	while(cin >> n >> c){
    		read();
    		compute();
    	}
    	return 0;
    } 
    
    

    信息

    ID
    290
    时间
    1000ms
    内存
    256MiB
    难度
    8
    标签
    (无)
    递交数
    75
    已通过
    12
    上传者