1 条题解

  • 0
    @ 2025-8-24 21:51:11

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar hongzy
    **

    搬运于2025-08-24 21:51:11,当前版本为作者最后更新于2018-12-27 18:50:52,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    首先,这题答案与切的顺序无关

    证明很好证,考虑将一块abcabc分为a,b,ca,b,c三块

    方法一:先分成a,bca,bc。答案为a(b+c)+bc=ab+ac+bca (b + c) + bc = ab + ac + bc

    方法二:先分成ab,cab,c。答案为c(a+b)+ab=ab+ac+bcc (a + b) + ab = ab + ac + bc

    答案相同。

    然后考虑dpdp方程:若s[x]=i=1xa[i]s[x]=\sum_{i=1}^x a[i],令f[i][j]f[i][j]表示前ii个数分jj次的最大得分

    $f[i][j]=min_{0\leq l<i}(f[l][j - 1] + s[l] * (s[i] - s[l]))$

    需要滚动数组存,以下用f[i]f[i]表示f[i][j1]f[i][j-1](前驱状态)

    然后考虑当iijj转移优于ll时:(以下默认j<lj < l

    $$f[j] + s[j]s[i] - s[j]^2 > f[l] + s[l]s[i] - s[l]^2 $$$$f[j] - s[j]^2 - (f[l] - s[l]^2) > (s[l]- s[j])s[i] $$$$\frac{f[j] - s[j]^2 - (f[l] - s[l]^2)}{- s[j] -(-s[l])} > s[i] $$

    把每个点看做Pi(s[i],f[i]s[i]2)P_i(-s[i],f[i]-s[i]^2),那么单调队列维护一个点集,斜率递增

    这些点形成了一个在第三象限的下凸包

    操作:

    队头更新:若slope(q[head],q[head+1])s[i]slope(q[head], q[head + 1]) \leq s[i]则出队(显然)

    队尾更新:若slope(q[back1],q[back])slope(q[back],i)slope(q[back - 1], q[back]) \geq slope(q[back], i)则出队

    队尾操作的证明(也就是对凸包的证明):

    考虑q[back]q[back]可不可能做队头。若满足刚刚上方的条件,则:

    slope(q[back],q[back1])s[i]slope(q[back],q[back-1]) \leq s[i]使得q[back]q[back]成了队首

    这时候当前的slopeslope比刚刚的还要小,因此将继续出队,q[back]q[back]无法做队头。

    斜率递增,故是一个凸包

    还有一个细节:注意可能会出现a[i]=0a[i]=0,导致存在s[i]=s[j]s[i] = s[j],因此计算斜率应注意横坐标相同的情况,斜率为-\infty.

    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    
    typedef long long LL;
    
    const int N = 1e5 + 10;
    
    int n, k, fa[205][N];
    LL s[N], f[N], g[N];
    
    double slope(int a, int b) {
    	LL x1 = - s[a], y1 = g[a] - s[a] * s[a];
    	LL x2 = - s[b], y2 = g[b] - s[b] * s[b];
    	if(x1 == x2) return -1e18;
    	return (y2 - y1) / (double) (x2 - x1);
    }
    
    int main() {
    	scanf("%d%d", &n, &k);
    	for(int i = 1; i <= n; i ++) {
    		scanf("%lld", s + i);
    		s[i] += s[i - 1];
    	}
    	static int q[N], hd, bk;
    	for(int t = 1; t <= k; t ++) {
    		for(int i = 1; i <= n; i ++) g[i] = f[i]; 
    		hd = bk = 0;
    		for(int i = 1; i <= n; i ++) {
    			for(; bk - hd >= 2 && slope(q[hd], q[hd + 1]) <= s[i]; hd ++) ;
    			f[i] = 0;
    			if(hd < bk) {
    				int & j = q[hd]; fa[t][i] = j;
    				f[i] = g[j] + s[j] * (s[i] - s[j]);
    			}
    			for(; bk - hd >= 2 && slope(q[bk - 1], q[bk - 2]) >= slope(i, q[bk - 1]); bk --) ;
    			q[bk ++] = i;
    		}
    	}
    	printf("%lld\n", f[n]);
    	for(int x = fa[k][n]; k; x = fa[-- k][x])
    		printf("%d ", x);
    	return 0;
    }
    

    upd: 感谢@chennengkuan 指出定义的小错误

    • 1

    信息

    ID
    1456
    时间
    2000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者