zoukankan      html  css  js  c++  java
  • [luogu4072] 征途

    题面

    题意体面中写得很明确, 应该不用我说了, 方差的概念在初中人教版九年级数学中有所提到, 没有上过初中的同学们可以左转百度.

    将序列拆为几段求最值, 我们考虑用dp来实现.

    先推一下式子, 令方差为(v), (r)为某一段路径的长度, (overline r)为这(m)条路径长度的平均数.

    则有:

    [egin{aligned} v &= frac{sum_{i=1}^{m}(overline r - r_i)^2}{m}\ &= frac{sum_{i=1}^{m}r_i^2 + m * (overline r)^2 - 2 * (overline r) * sum_{i=1}^{m}r_i}{m}\ &= frac{sum_{i=1}^{m}r_i^2 + m * (frac{sum_{i=1}^{m}r_i}{m})^2 - 2 * frac{(sum_{i=1}^{m}r_i)^2}{m}}{m}\ &=- frac{(sum_{i=1}^{m}r_i)^2}{m ^ 2} + frac{sum_{i=1}^{m}r_i^2}{m} end{aligned} ]

    答案所求为(v * m ^ 2), 所以:

    [egin{aligned} v * m ^ 2 &= m * sum_{i=1}^{m}r_i^2 - (sum_{i=1}^{m}r_i) ^ 2\ end{aligned} ]

    我们发现((sum_{i=1}^{m}r_i) ^ 2)是一个定值, 也就是总路径长度的平方, 所以我们只需要求前面那个数就可以了, 用前缀和先处理一下, 即(S_i)表示1 ~ i的路径的长度.

    我们设(f[i][k])表示前(i)段路花(k)天走的最小花费, 所以我们可以得到状态转移方程:

    [f[i][k] = min(f[i][k], f[j][k - 1] + (sum[j] - sum[i])^2) ]

    还是老套路, 设对(i)点选(t)((t) > (j))转移比选(j)转移更优, 则有:

    [egin{aligned} f[t][k - 1] + (sum[t] - sum[i])^2 &< f[j][k - 1] + (sum[j] - sum[i]) ^ 2\ (f[t][k - 1] + sum[t] ^ 2) - (f[j][k - 1] + sum[j] ^ 2) &< 2 * sum[i] * (sum[t] - sum[j])\ frac{(f[t][k - 1] + sum[t] ^ 2) - (f[j][k - 1] + sum[j] ^ 2)}{2 * (sum[t] - sum[j])} &< sum[i] end{aligned} ]

    由此可知道我们需要维护一个下凸包, 不信的话选三个点试一下就可以发现了, 然后愉快的推式子时间又结束了, 下面是大家喜(shen)闻(wu)乐(tong)见(jue)的(Coding Time)...

    代码

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define N 3005
    using namespace std;
    
    int n, m, sum[N], q[N], l, r;
    long long f[N], g[N]; 
    
    inline int read()
    {
    	int x = 0, w = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    	return x * w;
    }
    
    bool F_check(int x, int y, int z) { return 1ll * (g[y] - g[x] + sum[y] * sum[y] - sum[x] * sum[x]) < 1ll * 2 * z * (sum[y] - sum[x]); } 
    
    bool S_check(int x, int y, int z) { return (g[y] - g[x] + sum[y] * sum[y] - sum[x] * sum[x]) * (sum[z] - sum[y]) > (g[z] - g[y] + sum[z] * sum[z] - sum[y] * sum[y]) * (sum[y] - sum[x]); }
    
    int main()
    {
    	n = read(); m = read();
    	for(int i = 1; i <= n; i++) { sum[i] = read(); sum[i] += sum[i - 1]; g[i] = 1ll * sum[i] * sum[i]; }
    	for(int k = 1; k < m; k++)
    	{
    		l = 1; r = 0; q[++r] = k; 
    		for(int i = k + 1; i <= n; i++)
    		{
    			while(l < r && F_check(q[l], q[l + 1], sum[i])) l++;
    			f[i] = g[q[l]] + 1ll * (sum[i] - sum[q[l]]) * (sum[i] - sum[q[l]]);
    			while(l < r && S_check(q[r - 1], q[r], i)) r--;
    			q[++r] = i; 
    		}
    		for(int i = 1; i <= n; i++) g[i] = f[i]; 
    	}
    	printf("%lld
    ", m * f[n] - sum[n] * sum[n]); 
    	return 0;
    }
    

    不知道交了多少遍才AC的菜鸡我

  • 相关阅读:
    获取Enum枚举值描述的几法方法
    Android开发入门 Button事件实现的方法(原创)
    最有价值的.Net第三方控件
    Eclipse快捷键大全(转载)
    recovery教程
    XP系统通过无线网卡共享宽带给其他设备,正确的共享设置(修正版,绝对可行)
    使用Eclipse写QT
    Android 4.0模拟器弹出“谷歌拼音输入法”已停止运行的解决方法
    C# 枚举用法总结
    谷歌安卓系统使用必读,什么是root, Recovery, Radio, APP TO SD, Rom
  • 原文地址:https://www.cnblogs.com/ztlztl/p/10623347.html
Copyright © 2011-2022 走看看