zoukankan      html  css  js  c++  java
  • 【BZOJ3675】【Apio2014】序列分割

    Description

      
      传送门
      
      
      
        
      

    Solution

      
    ​  之前我也遇到过一次这种“两段之和乘积作为贡献“的问题:考虑把这一种((sum) *(sum))的形式拆括号,就可以发现贡献其实就是分别处于左右的两两元素乘积之和。
      
    ​  题目的分割(k)次,其实就是要你把序列分成(k+1)段。
      
    ​  再考虑在题目中如此的分割方法下,贡献是怎么产生的。对于每一个元素(a_i),每次分割时,若涉及到自己,则会贡献一定的乘积(a_i(sum))。细心想一想就会发现,这个(sum)的总值就是在最终方案下不处于(a_i)所在段的元素之和。
      
      单向考虑每一项乘积,(这里有点跳)总的来算,每一段([l,r])的贡献就是((sum_{i=l}^ra_i)(sum_{i=1}^{l-1}a_i))。记(a)的前缀和为(s),则贡献是((s_r-s_{l-1})s_{l-1}).
      
      我们可以写出DP式,(f_{i,j})表示前(i)个数恰好分成(j)段的贡献最大值:

    [f_{i,j}=max{f_{k,j-1}+(s_i-s_k)s_k};;(k<i) ]

       
      
      直接DP是(mathcal O((k+1)n^2))的。而看到这个式子比较简单,考虑斜率优化。这里省去第二维,整体做(k+1)次即可。
      
    ​  设(k<j<i)(j)(k)优,则有:

    [egin{aligned} f_j+(s_i-s_j)s_j&>f_{k}+(s_i-s_k)s_k\ f_j+s_is_j-s_j^2&>f_k+s_is_k-s_k^2\ s_i(s_j-s_k)&>(s_j^2-f_j)-(s_k^2-f_k)\ frac{(s_j^2-f_j)-(s_k^2-f_k)}{(s_j-s_k)}&<s_i end{aligned} ]

      直接做就可以了。
      
      
      
      
      

    Code

      

    #include <cstdio>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const int N=100005;
    const ll INF=1LL<<62;
    const double EPS=1e-6;
    int n,m,a[N];
    int q[N],head,tail;
    ll s[N],real_f[N],real_g[N],*f=real_f,*g=real_g;
    inline double slope(int a,int b){
    	return 1.0*((s[b]*s[b]-g[b])-(s[a]*s[a]-g[a]))/(s[b]-s[a]);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	int cnt=0;
    	for(int i=1,x;i<=n;i++){
    		scanf("%d",&x);
    		if(x)
    			cnt++,s[cnt]=s[cnt-1]+x;
    	}
    	n=cnt; m=min(m,n-1)+1;
    	for(int j=2;j<=m;j++){
    		q[head=tail=1]=j-1;
    		for(int i=j;i<=n;i++){
    			while(head<tail&&slope(q[head],q[head+1])-EPS<s[i]) head++;
    			int best=q[head];
    			f[i]=g[best]+(s[i]-s[best])*s[best];	
    			while(head<tail&&slope(q[tail-1],q[tail])>slope(q[tail],i)) tail--;
    			q[++tail]=i;
    		}
    		swap(f,g);
    	}
    	printf("%lld
    ",g[n]);
    	return 0;
    }
    

  • 相关阅读:
    您真的了解javaScript?(3)
    SvUDID实现设备唯一标示
    iOS 获取通讯录权限的时机
    iOS 后台退出app时不执行applicationWillTerminate的临时解决方法
    sqlite 批量插入, 重复插入(更新)
    tableView 显示区域偏移
    socket 粘包问题(转)
    ruby 安装更新
    openssl 升级
    gem 安装&卸载pod
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/9248746.html
Copyright © 2011-2022 走看看