zoukankan      html  css  js  c++  java
  • 【BZOJ4709】【Jsoi2011】柠檬

    Description

      
      传送门
      
      题意简述:将序列划分成任意多段,从每一段选出一个数(x),获得(在这一段出现的次数x*(x在这一段出现的次数))的贡献。求总贡献最大值。
      
      
      
      
      

    Solution

      
    ​  首先,要发现一个很重要的性质:如果某一段选了(x),那么这一段一定是以(x)开头、以(x)结尾的一段。否则,可以将此段缩减至以(x)开头、以(x)结尾的更小的一段,虽然贡献没有变,但留给其他段的机会更多。
      
      设(f_i)表示(1...i)的贡献最大值。记(a_i)表示(i)的数值,(b_i)表示(a_i)在相同的值中是第几个出现的。显然如果要从别的(f_j)转移到(f_i),必须满足(a_{j+1}==a_i)。我们有转移方程:

    [f_i=max {f_{j-1}+a_i(b_i-b_j+1)^2};;;jle i,a_j=a_i ]

      设(j)为最优转移点:

    [egin{aligned} f_i&=f_{j-1}+a_i(b_i-(b_j-1))^2\ f_i&=f_{j-1}+a_i(b_i^2-2b_i(b_j-1)+(b_j-1)^2)\ f_i&=f_{j-1}+a_ib_i^2-2a_ib_i(b_j-1)+a_i(b_j-1)^2\ f_{j-1}+a_i(b_j-1)^2&=2a_ib_i(b_j-1)+f_i-a_ib_i^2 end{aligned} ]

      这其实是一个直线的式子:(k=2a_ib_i)(x=(b_j-1))(b=(f_i-a_ib_i^2))(y=f_{j-1}+a_i(b_j-1)^2).
      
      其中(a_i)看似和(i)有关,无法继续推理。但由于转移的(j)满足(a_j=a_i),所以每一个位置的数在参与上述DP时,相关联的(a)其实就是每一个元素自己的数值,是一个定值。
      
      把每一个元素看成二维平面的一个点((x,y))。由于最优转移相当于最大化截距,那么最优转移点(j)可以看做在斜率为(k)的时候上凸包碰到的第一个点。
      
      那么我们扫描序列时,维护每一个数值对应的上凸包,每次查询时在上面二分即可。
      
      时间复杂度(mathcal O(n lg n))
      
      当然,也可以用斜率优化直接做。
      
      
      

    Code

      

    #include <cstdio>
    #include <vector>
    #define k(i) (2LL*a[i]*b[i])
    #define x(i) (b[i]-1LL)
    #define y(i) (f[i-1]+1LL*a[i]*(b[i]-1)*(b[i]-1))
    #define b(i) (f[i]-1LL*a[i]*b[i]*b[i])
    #define pb push_back
    #define db pop_back
    using namespace std;
    typedef long long ll;
    const int N=100005,S=10005;
    const double EPS=1e-6;
    int n,a[N],ecnt[S],b[N];
    ll f[N];
    vector<int> s[S];
    int slen[S];
    double slope(int u,int v){return 1.0*(y(v)-y(u))/(x(v)-x(u));}
    int query(int col,int k){
    	k=2*col*k;
    	int l=0,r=slen[col]-2,mid;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(slope(s[col][mid],s[col][mid+1])-EPS<=k) r=mid-1;
    		else l=mid+1;
    	}
    	return s[col][l];
    }
    void insert(int col,int i){
    	int sz=slen[col];
    	while(sz>1&&slope(s[col][sz-2],s[col][sz-1])<slope(s[col][sz-1],i)) 
    		sz--,slen[col]--,s[col].db();
    	s[col].pb(i);
    	slen[col]++;
    }
    int main(){
    	scanf("%d",&n); 
    	for(int i=1;i<=n;i++){
    		scanf("%d",a+i);
    		b[i]=++ecnt[a[i]];
    	}
    	for(int i=1;i<=n;i++){
    		insert(a[i],i);
    		int j=query(a[i],b[i]);
    		f[i]=(j?f[j-1]:f[i-1])+1LL*a[i]*(b[i]-b[j]+1)*(b[i]-b[j]+1);
    	}
    	printf("%lld
    ",f[n]);
    	return 0;
    }
    
  • 相关阅读:
    一些 Ubuntu 使用的小技巧
    体验 Web 自动化测试工具 Selenium
    CentOS 7 上安装 Nginx
    Windows查看端口占用情况
    Windows远程登录提醒:由于没有远程桌面授权服务器可以提供许可证,远程会话连接已断开。请跟服务器管理员联系。
    Vue动态的改变css样式
    centos7 U盘安装卡在 starting dracut initqueue hook Reached target Basic System
    用tsc编译ts文件的时候报错,tsc : 无法加载文件,因为在此系统上禁止运行脚本;
    Linux修改SSH默认的端口号
    Centos编译安装新版本Git
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/9249338.html
Copyright © 2011-2022 走看看