概述
题目: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题题解?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复