我是靠谱客的博主 儒雅树叶,最近开发中收集的这篇文章主要介绍Codeforce#547 Div3 F题题解?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

题目:http://codeforces.com/contest/1141/problem/F2

一开始看数据规模,1500,啊好友好,直接F2吧……
多半是前面的【水题】做的膨胀了……
然后滚回F1了。
是这样的,春季校赛不是有个异或前缀和dp吗?也是选一些互不重合的连续区间,求区间异或和最大的数量。异或和和一般的和不是一样的吗?那敢情好,b[i]表示到i为止的前缀和,f[i]表示到i为止区间和为t的互不相交的最大区间数,dp时在i前面找一个j,使f[j]==f[i-1](保证j+1没有被覆盖过)且b[i]-b[j]==t(此时选定区间为[j+1,i]),由于b中元素范围比较大,用map维护b中元素的值对应的最后一个元素的下标,在map中找t-b[i]即可,O(nlogn)的dp。
那么问题来了,t是多少呢?
按数值枚举是不可能的,只能枚举每一种区间作为答案,有n^2种可能。总时间复杂度是O(n^3logn)。瞬间被打回原型。

    #include <bits/stdc++.h>
    using namespace std;
    long long a[4000],b[4000],f[3100][3100],dp[4000],g[4000];
    int main (/*int argc, char const* argv[]*/){
    	std::ios::sync_with_stdio(false);
    	int n;cin>>n;
    	for(int i=1;i<=n;i+=1){
    		cin>>a[i];
    		b[i]=b[i-1]+a[i];
    	}
    	for(int i=1;i<=n;i+=1){
    		for(int j=i;j<=n;j+=1){
    			f[i][j]=b[j]-b[i-1];
    		}
    	}
    	vector<pair<int,int>>ans;
    	for(int i=1;i<=n;i+=1){
    		for(int j=i;j<=n;j+=1){
    			int t=f[i][j];
    			vector<pair<int,int>>pans;
    			dp[0]=0;
    			map<int,int>st;
    			st[0]=0;
    			for(int k=1;k<=n;++k){
    				dp[k]=dp[k-1];
    				if(st.find(b[k]-t)!=st.end()){
    					int u=st[b[k]-t];
    					if(dp[u]==dp[k]){
    						++dp[k];
    						pans.push_back(make_pair(u+1,k));
    					}
    				}
    				st[b[k]]=k;
    			}
    			if(ans.size()<pans.size())ans=pans;
    		}
    	}
    	cout<<ans.size()<<endl;
    	for(auto i:ans){
    		cout<<i.first<<' '<<i.second<<endl;
    	}
    	
    	return 0;
    }

我不知道这种方法还能不能继续优化,如果要优化很可能是对外层i和j动手脚,或者和dp时的k联系起来,但本人实在想不到了……如果有人想到这种做法的优化,请务必联系本菜鸡……

于是推翻重来:
感谢Acoder巨佬提供的状态枚举思路,f[i][j]代表选取以区间[i,j]为最后一个区间的方案最大值。那么只要在之前的区间中找一对[p,q],其中q<i,使b[q]-b[p-1]=f[j]-f[i-1]且f[p][q]尽量大。
还是用一个叫st的map<int,pair>维护区间和,这里st[m]=<p,q>表示目前为止区间和为m的各个区间中,f[p][q]最大q尽量小的那个。
什么意思呢?首先,由于枚举了i,j,我们希望能在O(logn)的时间复杂度内完成转移。那么,转移的来源是什么?就是st[m]。但前提是必须保证转移的区间不重合,即st[m]的右端点q必须小于当前的左端点i!
如果f[i][j]不能由当前的st[m]转移,意味着什么?也许f[i][j]可以由更之前的区间转移过来,但回想st[m]的定义:st[m]=<p,q>,其中[p,q]是当前所有和为m的区间中f[p,q]最大的那个,且在此前提下q尽量的小。如果f[i][j]不能被f[p][q]转移,那么f[i][j]也一定不能由其他的和为m,且f值大于等于f[p][q]的区间转移过来。换句话说,即使f[i][j]被某个区间转移过来,它的值一定小于等于f[p][q]。而我们希望在相同f值的前提下,st[m]的右端点q尽量小;而j一定大于q。所以,此时的区间[i,j]如同【银背族长·詹姆斯】,被值不比它小,还比它靠左的区间[p,q]完爆了!所以,此时我们干脆不要管f[i][j]了,反正统计答案也轮不到它。所以修改f[i][j]的定义:f[i][j]代表选取以区间[i,j]为最后一个区间的方案最大值,但被前面的某个区间”完爆”者不算在内
显然,先枚举右端点j,再枚举左端点i,所得到的f[i,j]的答案一定是相同答案中右端点最小的。而且f[i][j]如果成功被map[m]转移,那么显然f[i][j]一定是当前区间和为m的区间中答案最大的区间。所以只要f[i][j]能被转移,就用[i,j]更新map[m],从而维护了map。另外,如果在map中找不到m,意味着[i,j]是区间和为m的第一个区间,令之为1并更新map即可。

    //模板嵌套不打空格,低版本c++CE注意
    #include <bits/stdc++.h>
    using namespace std;
    int a[2000],b[2000];
    struct msp{
    	int lasti,lastj,data;
    }f[2000][2000];
    int main (/*int argc, char const* argv[]*/){
    	std::ios::sync_with_stdio(false);
    	int n;cin>>n;
    	for(int i=1;i<=n;i+=1){
    		cin>>a[i];
    		b[i]=b[i-1]+a[i];
    	}
    	map<int,pair<int,int>>st;//st[i]means the maxsize of numbers of blocks with sum=i while r keep the min
    	for(int j=1;j<=n;j+=1){//j first
    		for(int i=1;i<=j;i+=1){
    			if(st.find(b[j]-b[i-1])!=st.end()){
    				pair<int,int>t=st[b[j]-b[i-1]];
    				if(t.second<i){
    					f[i][j].data=f[t.first][t.second].data+1,f[i][j].lasti=t.first,f[i][j].lastj=t.second;//lasti,lastj record the answer
    					st[b[j]-b[i-1]]=make_pair(i,j);
    				}
    			}
    			else f[i][j].data=1,st[b[j]-b[i-1]]=make_pair(i,j);
    		}
    	}
    	int fi,fj,maxm=0;
    	for(int i=1;i<=n;i+=1){
    		for(int j=i;j<=n;j+=1){
    			if(f[i][j].data>maxm){
    				maxm=f[i][j].data,fi=i,fj=j;
    			}
    		}
    	}
    	cout<<maxm<<endl;
    	while(fi){
    		cout<<fi<<' '<<fj<<endl;
    		int ti=f[fi][fj].lasti,tj=f[fi][fj].lastj;
    		fi=ti,fj=tj;
    	}
    	return 0;
    }

以上。

最后

以上就是儒雅树叶为你收集整理的Codeforce#547 Div3 F题题解?的全部内容,希望文章能够帮你解决Codeforce#547 Div3 F题题解?所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部