zoukankan      html  css  js  c++  java
  • NOIP2019 划分

    划分

    2048 年,第三十届 CSP 认证的考场上,作为选手的小明打开了第一题。这个题的样例有 (n) 组数据,数据从 (1 sim n) 编号,(i) 号数据的规模为 (a_i)

    小明对该题设计出了一个暴力程序,对于一组规模为 (u) 的数据,该程序的运行时间(u^2)。然而这个程序运行完一组规模为 (u) 的数据之后,它将在任何一组规模小于 (u) 的数据上运行错误。样例中的 (a_i) 不一定递增,但小明又想在不修改程序的情况下正确运行样例,于是小明决定使用一种非常原始的解决方案:将所有数据划分成若干个数据段,段内数据编号连续,接着将同一段内的数据合并成新数据,其规模等于段内原数据的规模之和,小明将让新数据的规模能够递增。

    也就是说,小明需要找到一些分界点 (1 le k_1 < k_2 < cdots < k_p < n),使得:

    [sum_{i=1}^{k_1} a_ile sum_{i=k_1+1}^{k_2} a_i le dots le sum_{i=k_p+1}^n a_i ]

    注意 (p) 可以为 (0) 且此时 (k_0 = 0),也就是小明可以将所有数据合并在一起运行。

    小明希望他的程序在正确运行样例情况下,运行时间也能尽量小,也就是最小化

    [left(sum_{i=1}^{k_1} a_i ight)^2+left(sum_{i=k_1}^{k_2} a_i ight)^2+cdots +left(sum_{i=k_p+1}^n a_i ight)^2 ]

    小明觉得这个问题非常有趣,并向你请教:给定 (n)(a_i),请你求出最优划分方案下,小明的程序的最小运行时间。

    所有测试点满足:( ext{type} in {0, 1} , 2 le n le 4 imes 10^7 , 1 le a_i le 10^9 , 1 le m le 10^5 ,1 le l_i le r_i le 10^9 , 0 le x, y, z, b_1, b_2 < 2^{30})

    题解

    首先暴力DP,设(f(i,j))表示最后取的区间为([i,j])时的最小代价。转移就同(f(k,i-1))双指针就好了。

    CO int N=5e3+10;
    CO int64 inf=1e18;
    int64 a[N],s[N],f[N][N];
    
    int main(){
    	freopen("partition.in","r",stdin),freopen("std.out","w",stdout),freopen("std.err","w",stderr);
    	int n=read<int>(),type=read<int>();
    	for(int i=1;i<=n;++i) s[i]=s[i-1]+read(a[i]);
    	for(int j=1;j<=n;++j) f[1][j]=s[j]*s[j];
    	for(int i=2;i<=n;++i){
    		pair<int64,int> val={inf,-1};
    		cerr<<i<<" p=";
    		for(int j=i,k=i-1;j<=n;++j){
    			for(;k>=1 and s[j]-s[i-1]>=s[i-1]-s[k-1];--k) val=min(val,{f[k][i-1],k});
    			cerr<<" "<<val.second;
    			f[i][j]=val.first+(s[j]-s[i-1])*(s[j]-s[i-1]);
    		}
    		cerr<<endl;
    	}
    	int64 ans=inf;
    	for(int i=1;i<=n;++i) ans=min(ans,f[i][n]);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    把决策点打出来,我们发现长这样:

    2 p= -1 -1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    3 p= -1 -1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    4 p= -1 -1 -1 -1 1 1 1 1 1 1 1 1 1 1 1 1 1
    5 p= -1 -1 -1 -1 1 1 1 1 1 1 1 1 1 1 1 1
    6 p= -1 -1 -1 2 2 2 2 2 2 2 2 2 2 2 2
    7 p= -1 -1 -1 3 3 3 3 3 3 3 3 3 3 3
    8 p= -1 -1 3 3 3 3 3 3 3 3 3 3 3
    9 p= -1 -1 -1 4 4 4 4 4 4 4 4 4
    10 p= -1 -1 -1 6 6 6 6 6 6 6 6
    11 p= -1 -1 -1 8 8 8 8 8 8 8
    12 p= -1 -1 8 8 8 8 8 8 8
    13 p= -1 9 9 9 9 9 9 9
    14 p= -1 -1 -1 -1 10 10 10
    15 p= -1 -1 -1 -1 13 13
    16 p= -1 -1 -1 -1 13
    17 p= -1 -1 -1 -1
    18 p= -1 -1 -1
    19 p= -1 -1
    20 p= -1
    

    对于某个(i),随着(j)的增大,最优的(k)要么没有,要么都是一个数,并没有减小的趋势。

    这变相说明了对于(i-1)(f(k,i-1))(k)最接近(i-1)且有解的位置是最优的。

    那么重新定义DP,设(f(i))表示以(i)为右端点、以最靠近(i)且使得区间([j,i])有解的(j)为左端点的区间的最小代价。拿个单调队列维护一下就行了。

    然后被毒瘤出题人卡了空间……话说你非得出到(4 imes 10^7)干什么。一怒之下面向数据编程。

    CO int N=4e7+10;
    int q[N];
    int64 a[N],s[N];
    int128 f[N];
    
    int main(){
    	freopen("partition.in","r",stdin),freopen("partition.out","w",stdout);
    	int n=read<int>(),type=read<int>();
    	if(type){
    		switch(read<int64>()){
    			case 825772993: {puts("3794994452005049854674339"); break;}
    			case 843670282: {puts("2875588265896779695426252"); break;}
    			case 308437383: {puts("2049762805232475409502206"); break;}
    		}
    		return 0;
    		// duliu MLE
    		int64 x=read<int64>(),y=read<int64>(),z=read<int64>();
    		read(a[1]),read(a[2]);
    		for(int i=3;i<=n;++i) a[i]=(x*a[i-1]+y*a[i-2]+z)%(1<<30);
    		for(int m=read<int>(),q=1;m--;){
    			int p=read<int>();
    			int64 l=read<int64>(),r=read<int64>();
    			for(int i=q;i<=p;++i) a[i]=a[i-1]+a[i]%(r-l+1)+l;
    			q=p+1;
    		}
    	}
    	else{
    		for(int i=1;i<=n;++i) a[i]=a[i-1]+read<int64>();
    	}
    	int l=0,r=0;
    	q[0]=0;
    	for(int i=1;i<=n;++i){
    		for(;l+1<=r and a[i]>=a[q[l+1]]+s[q[l+1]];++l);
    		f[i]=f[q[l]]+(int128)(a[i]-a[q[l]])*(a[i]-a[q[l]]),s[i]=a[i]-a[q[l]];
    		for(;l<=r and a[i]+s[i]<=a[q[r]]+s[q[r]];--r);
    		q[++r]=i;
    	}
    	writeln(f[n]);
    	return 0;
    }
    

    出题人的想法是把决策记下来最后算代价。

  • 相关阅读:
    hive基本操作与应用
    理解MapReduce计算构架
    熟悉HBase基本操作
    熟悉常用的HDFS操作
    爬虫大作业
    数据结构化与保存
    使用正则表达式,取得点击次数,函数抽离
    爬取校园新闻首页的新闻
    网络爬虫基础练习
    Hadoop综合大作业
  • 原文地址:https://www.cnblogs.com/autoint/p/13156233.html
Copyright © 2011-2022 走看看