首页 > 代码库 > [SCOI2010]序列操作[分块or线段树]
[SCOI2010]序列操作[分块or线段树]
/*本题的难度在于标记的下放。下面说一下我的做法: 1.覆盖标记:直接打上就好了2.取反标记: <1>如果有tag标记,将tag标记取反,退出. <2>如果有rev标记,直接退出 <3>无标记,打上rev标记,退出维护:sum(当前区间和),lss1(区间从左端点连续1的长度),rss1(区间从右端点连续1的长度),sc1(区间连续1的长度)lss0,rss0,sc0同理。tag(覆盖标记和取反标记整一起了) */#include<cmath>#include<cstdio>#include<cstring>#include<iostream>using namespace std;const int N=1e5+5;const int M=505;int a[N],bl[N],n,m,size;int tag[M],lss0[M],rss0[M],sc0[M],lss1[M],rss1[M],sc1[M],sum[M];void init(int i){ tag[i]=-1;lss0[i]=rss0[i]=sc0[i]=lss1[i]=rss1[i]=sc1[i]=sum[i]=0; for(int j=(i-1)*size+1;j<=min(n,i*size);j++) if(a[j]) sum[i]++; int fl=0,p=0; for(int j=(i-1)*size+1;j<=min(n,i*size);j++){ if(!a[j]&&!fl) lss0[i]++; else fl=1; if(!a[j]) p++;else p=0; sc0[i]=max(sc0[i],p); } fl=0; for(int j=min(n,i*size);j>(i-1)*size;j--){ if(!a[j]&&!fl) rss0[i]++; else fl=1; } fl=0,p=0; for(int j=(i-1)*size+1;j<=min(n,i*size);j++){ if(a[j]&&!fl) lss1[i]++; else fl=1; if(a[j]) p++;else p=0; sc1[i]=max(sc1[i],p); } fl=0; for(int j=min(n,i*size);j>(i-1)*size;j--){ if(a[j]&&!fl) rss1[i]++; else fl=1; }}void pushdown(int i){ if(tag[i]==-1) return; if(tag[i]==0||tag[i]==1) for(int j=(i-1)*size+1;j<=i*size;j++) a[j]=tag[i]; else for(int j=(i-1)*size+1;j<=i*size;j++) a[j]^=1; tag[i]=-1;}void cover(int x,int y,int v){ pushdown(bl[x]); for(int i=x;i<=min(y,bl[x]*size);i++) a[i]=v; init(bl[x]); for(int i=bl[x]+1;i<bl[y];i++){ tag[i]=v; if(v) lss1[i]=rss1[i]=sc1[i]=sum[i]=size,lss0[i]=rss0[i]=sc0[i]=0; else lss1[i]=rss1[i]=sc1[i]=sum[i]=0,lss0[i]=rss0[i]=sc0[i]=size; } if(bl[x]==bl[y]) return; pushdown(bl[y]); for(int i=(bl[y]-1)*size+1;i<=y;i++) a[i]=v; init(bl[y]);}void rever(int x,int y){ pushdown(bl[x]); for(int i=x;i<=min(y,bl[x]*size);i++) a[i]^=1; init(bl[x]); for(int i=bl[x]+1;i<bl[y];i++){ if(tag[i]==-1) tag[i]=2; else if(tag[i]==0) tag[i]=1; else if(tag[i]==1) tag[i]=0; else tag[i]=-1; swap(lss0[i],lss1[i]);swap(rss0[i],rss1[i]); swap(sc0[i],sc1[i]);sum[i]=size-sum[i]; } if(bl[x]==bl[y]) return; pushdown(bl[y]); for(int i=(bl[y]-1)*size+1;i<=y;i++) a[i]^=1; init(bl[y]);}int query_sum(int x,int y){ int ans=0; pushdown(bl[x]); for(int i=x;i<=min(y,bl[x]*size);i++) if(a[i]) ans++; for(int i=bl[x]+1;i<bl[y];i++) ans+=sum[i]; if(bl[x]==bl[y]) return ans; pushdown(bl[y]); for(int i=(bl[y]-1)*size+1;i<=y;i++) if(a[i]) ans++; return ans;}int query_num(int x,int y){ int ans=0,l=0; pushdown(bl[x]); for(int i=x;i<=min(y,bl[x]*size);i++){ if(a[i]) l++;else l=0; ans=max(ans,l); } for(int i=bl[x]+1;i<bl[y];i++){ l+=lss1[i]; ans=max(ans,l); ans=max(ans,sc1[i]); if(lss1[i]!=size) l=rss1[i]; } if(bl[x]==bl[y]) return max(ans,l); pushdown(bl[y]); for(int i=(bl[y]-1)*size+1;i<=y;i++){ if(a[i]) l++;else l=0; ans=max(ans,l); } return ans;}int main(){ memset(tag,-1,sizeof(tag)); scanf("%d%d",&n,&m); size=sqrt(n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); bl[i]=(i-1)/size+1; } for(int i=1;i<=bl[n];i++) init(i); for(int i=1,op,x,y;i<=m;i++){ scanf("%d%d%d",&op,&x,&y);x++;y++; if(op==0) cover(x,y,0); if(op==1) cover(x,y,1); if(op==2) rever(x,y); if(op==3) printf("%d\n",query_sum(x,y)); if(op==4) printf("%d\n",query_num(x,y)); } return 0;}
[SCOI2010]序列操作[分块or线段树]
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。