• [洛谷P4072] SDOI2016 征途


    问题描述

    Pine开始了从S地到T地的征途。

    从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。

    Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。

    Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。

    帮助Pine求出最小方差是多少。

    设方差是v,可以证明,(v imes m^2)是一个整数。为了避免精度误差,输出结果时输出(v imes m^2)

    输入格式

    第一行两个数 n、m。

    第二行 n 个数,表示 n 段路的长度

    输出格式

    一个数,最小方差乘以 (m^2) 后的值 。

    样例输入

    5 2
    1 2 5 8 6

    样例输出

    36

    说明

    对于 (30\%) 的数据,(1 le n le 10)

    对于 (60\%) 的数据,(1 le n le 100)

    对于 (100\%) 的数据,(1 le n le 3000)

    保证从 S 到 T 的总路程不超过 30000 。

    解析

    首先,我们需要化简方差的式子,

    [egin{align}s^2 &=frac{sum_{i=1}^{m}(overline v-v_i)^2}{m}\ &=frac{moverline v^2-2overline v(v_1+v_2+...+v_m)+(v_1^2+v_2^2+...+v_m^2)}{m}\ &=frac{mfrac{(sum_{i=1}^{m}v_i)^2}{m^2}-2frac{(sum_{i=1}^{m}v_i)^2}{m}+v_1^2+v_2^2+...+v_m^2}{m}\end{align} ]

    所以

    [s^2 imes m^2=-(sum_{i=1}^{m}v_i)^2+m(v_1^2+...+v_m^2) ]

    所以,我们需要把路程划分为m个部分,使(v_1^2+...+v_m^2)最小。这个可以用动态规划来完成。设(f[i][j])表示将前i个数划分成j段的最小值。我们有如下状态转移方程:

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

    然后这个转移方程可以用斜率优化。

    代码

    #include <iostream>
    #include <cstdio>
    #define int long long
    #define N 3002
    using namespace std;
    int n,m,i,j,v[N],sum[N],f[N][N],q[N],head,tail;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    double k(int x,int i,int j)
    {
    	return 1.0*((f[i][x]+sum[i]*sum[i])-(f[j][x]+sum[j]*sum[j]))/(sum[i]-sum[j]);
    }
    signed main()
    {
    	n=read();m=read();
    	for(i=1;i<=n;i++){
    		v[i]=read();
    		sum[i]=sum[i-1]+v[i];
    		f[i][1]=sum[i]*sum[i];
    	}
    	for(j=2;j<=m;j++){
    		head=tail=1;
    		q[1]=j-1;
    		for(i=j;i<=n;i++){
    			while(head<tail&&k(j-1,q[head],q[head+1])<2*sum[i]) head++;
    			int x=q[head];
    			f[i][j]=f[x][j-1]+(sum[i]-sum[x])*(sum[i]-sum[x]);
    			while(head<tail&&k(j-1,q[tail],i)<k(j-1,q[tail],q[tail-1])) tail--;
    			q[++tail]=i;
    		}
    	}
    	printf("%lld
    ",m*f[n][m]-sum[n]*sum[n]);
    	return 0;
    }
    
  • 相关阅读:
    简单两行,实现无线WiFi共享上网,手机抓包再也不用愁了
    Windows下Python 3.6 安装BeautifulSoup库
    RSA加密算法破解及原理
    干货,Wireshark使用技巧-过滤规则
    干货:Wireshark使用技巧-显示规则
    干货!链家二手房数据抓取及内容解析要点
    Wireshark分析实战:某达速递登录帐号密码提取
    协议分析中的TCP/IP网络协议
    Wireshark使用教程:不同报文颜色的含义
    VMware kali虚拟机环境配置
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11877823.html
Copyright © 2020-2023  润新知