• 【题解】[SDOI2016]征途


    Link

    题目大意:给定序列,将它划分为(m)段使得方差最小,输出(s^2*m^2)(一个整数)。

    ( ext{Solution:})

    这题我通过题解中的大佬博客学到了一般化方差柿子的写法。

    下面来推柿子:

    [s^2=frac{sum_{i=1}^n (x_i-overline{x})^2}{n}=frac{1}{n}(sum_{i=1}^n x_i^2+n*(frac{ sum_{i=1}^n x_i}{n})^2-2sum_{i=1}^n (x_i* frac{sum_{k=1}^n x_k}{n})) ]

    [s^2=frac{sum_{i=1}^n x_i^2+frac{(sum_{i=1}^n x_i)^2}{n}-2 frac{(sum_{k=1}^n x_i)^2}{n}}{n} ]

    化简得到:

    [s^2=frac{(x_1^2+...+x_n^2)-frac{(sum_{i=1}^n x_i)^2}{n}}{n} ]

    两边乘以(n^2)得到:

    [s^2n^2=nsum_{i=1}^n x_i^2 -(sum_{i=1}^n x_i)^2=nsum_{i=1}^n x_i^2 -sum[n]^2 ]

    其中(sum)是前缀和。最后这个柿子里面,(n,sum)都是常数,最终要处理的就是(sum_{i=1}^n x_i^2).

    (dp[i][l])表示前(i)个元素划分(l)次的最小平方和,有:

    [dp[i][l]=min_{j<i}dp[j][l-1]+(sum[i]-sum[j])^2 ]

    [dp[i][l]=dp[j][l-1]+sum[i]^2+sum[j]^2-2sum[i]sum[j] ]

    [dp[j][l-1]+sum[j]^2=2sum[i]sum[j]+dp[i][l]-sum[i]^2 ]

    [y=dp[j][l-1]+sum[j]^2,k=2sum[i],x=sum[j],b=dp[i][l]-sum[i]^2 ]

    最终目的最小化(dp[i][l])这里就是最小化(b),观察到(2sum[i])这个斜率单调递增,所以我们维护所有大于这个斜率的决策点,做到(O(n).)

    对于这个题,还可以滚动数组优化,虽然这里不需要。

    几个实现细节:前(i)个元素可以划分成(i)段,所以每次枚举起点,它的决策起点应该是划分段数(-1),开始应该是划分段数对应的元素数。因为再往前往后都会导致不合法。

    ( ext{slope})的时候最好用( ext{long double}).顺序不要搞反。当然这个题主要难点是推方差柿子……( ext{WCSL.})

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,m,a[20010],sum[20010];
    int dp[4000][4000],tail,head;
    int q[200010];
    int X(int x){return sum[x];}
    int Y(int x,int p){return dp[x][p-1]+sum[x]*sum[x];}
    long double slope(int x,int y,int p){return (long double)(Y(y,p)-Y(x,p))/(X(y)-X(x));}
    //dp[i][l]=dp[j][l-1]+(sum[i]-sum[j])^2
    signed main(){
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;++i)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i],dp[i][1]=sum[i]*sum[i];
    	for(int p=2;p<=m;p++){
    		head=tail=1;
    		q[head]=p-1;
    		for(int i=p;i<=n;++i){
    			while(head<tail&&slope(q[head],q[head+1],p)<2.0*sum[i])head++;
    			dp[i][p]=dp[q[head]][p-1]+(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]]);
    			while(head<tail&&slope(q[tail-1],q[tail],p)>slope(q[tail-1],i,p))tail--;
    			q[++tail]=i;
    		}
    	}
    	printf("%lld
    ",m*dp[n][m]-sum[n]*sum[n]);
    	return 0;
    }
    

    附上推柿子时( ext{word})上的东西:

    (Dp[i][l]=dp[j][l-1]+(sum[i]-sum[j])^2)
    (Dp[i]][l]=dp[j][l-1]+sum[i]^2+sum[j]^2-2sum[i]sum[j])
    (Dp[j][l-1]+sum[j]^2=2sum[i]sum[j]+dp[i][l]-sum[i]^2)
    (Y=dp[j][l-1]+sum[j]^2,k=2sum[i],x=sum[j],b=dp[i][l]-sum[i]^2)
    最小化(b),即可

    (Ans=-sum[n]^2+m*dp[n][m])

  • 相关阅读:
    WINDOWS REDIS 修改requirepass 不生效;
    解读JavaScript原型链
    禁止浏览器自动填写用户名密码
    Vue购物车实例
    scrollTop的兼容性
    jQuery架构(源码)分析
    web前端优化整理(转)
    前端模块化:RequireJS(转)
    前端构建之gulp与常用插件(转载)
    PS快捷键
  • 原文地址:https://www.cnblogs.com/h-lka/p/12828645.html
Copyright © 2020-2023  润新知