【分块】bzoj1858 [Scoi2010]序列操作
2014-10-23 15:26
267 查看
分块 Or 线段树 分块的登峰造极之题
每块维护8个值:
包括左端点在内的最长1段;
包括右端点在内的最长1段;
该块内的最长1段;
该块内1的个数;
包括左端点在内的最长0段;//这四个是因为可能有翻转操作,需要swap 0有关的标记 和 1有关的标记
包括右端点在内的最长0段;
该块内的最长0段;
该块内0的个数。
2个懒标记:是否翻转,覆盖成了什么。
怎么处理一个块上有两个标记的情况呢?
若该块原来没有任何标记,或要打的标记和原本的标记种类相同,则直接打上标记;
若已有翻转标记,再覆盖时则先清除翻转标记,再打上覆盖标记;
若已有覆盖标记,再翻转时,则直接将覆盖标记取反。
So 某个块上同时只会有1个标记。
代码能力题,同样无法体现分块相对于线段树的“代码简介优势”,不推荐写。耗时也多于线段树。
P.S.注意取最长段的时候的细节……如下代码中高亮部分。
P.S.P.S.注意Pushdown的时机。
每块维护8个值:
包括左端点在内的最长1段;
包括右端点在内的最长1段;
该块内的最长1段;
该块内1的个数;
包括左端点在内的最长0段;//这四个是因为可能有翻转操作,需要swap 0有关的标记 和 1有关的标记
包括右端点在内的最长0段;
该块内的最长0段;
该块内0的个数。
2个懒标记:是否翻转,覆盖成了什么。
怎么处理一个块上有两个标记的情况呢?
若该块原来没有任何标记,或要打的标记和原本的标记种类相同,则直接打上标记;
若已有翻转标记,再覆盖时则先清除翻转标记,再打上覆盖标记;
若已有覆盖标记,再翻转时,则直接将覆盖标记取反。
So 某个块上同时只会有1个标记。
代码能力题,同样无法体现分块相对于线段树的“代码简介优势”,不推荐写。耗时也多于线段树。
P.S.注意取最长段的时候的细节……如下代码中高亮部分。
P.S.P.S.注意Pushdown的时机。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int len[400],lenl[400],lenr[400],n,m,l[400],r[400],num[100001],sum,sz,x,y; bool a[100001]; int len0[400],lenl0[400],lenr0[400],sum1[400],sum0[400]; int delta[400],op; bool Spin[400]; int Res,Num;char C,CH[12]; inline int G() { Res=0;C='*'; while(C<'0'||C>'9')C=getchar(); while(C>='0'&&C<='9'){Res=Res*10+(C-'0');C=getchar();} return Res; } inline void P(long long x) { Num=0;if(!x){putchar('0');puts("");return;} while(x>0)CH[++Num]=x%10,x/=10; while(Num)putchar(CH[Num--]+48); puts(""); } void Pushdown(const int &p) { if(delta[p]!=-1) { for(int i=l[p];i<=r[p];i++) a[i]=delta[p]; delta[p]=-1; } else if(Spin[p]) { for(int i=l[p];i<=r[p];i++) a[i]^=1; Spin[p]=false; } } inline void Work(const int &Lb,const int &Rb,const int &sym) { if(sym==0||sym==1) {for(int i=Lb;i<=Rb;i++) a[i]=sym;} else if(sym==2) {for(int i=Lb;i<=Rb;i++) a[i]^=1;} int cnt=0; for(int i=l[num[Lb]];i<=r[num[Lb]];i++) {if(a[i]) break; cnt++;}//暴力lenl0 lenl0[num[Lb]]=cnt; cnt=0; for(int i=r[num[Lb]];i>=l[num[Lb]];i--) {if(a[i]) break; cnt++;}//暴力lenr0 lenr0[num[Lb]]=cnt; cnt=0; int Longest=0; for(int i=l[num[Lb]];i<=r[num[Lb]];i++)//暴力len0 { if(a[i]) cnt=0; else cnt++; if(cnt>Longest) Longest=cnt; } len0[num[Lb]]=Longest; cnt=0; for(int i=l[num[Lb]];i<=r[num[Lb]];i++) {if(!a[i]) break; cnt++;}//暴力lenl lenl[num[Lb]]=cnt; cnt=0; for(int i=r[num[Lb]];i>=l[num[Lb]];i--) {if(!a[i]) break; cnt++;}//暴力lenr lenr[num[Lb]]=cnt; cnt=0; Longest=0; for(int i=l[num[Lb]];i<=r[num[Lb]];i++)//暴力len { if(!a[i]) cnt=0; else cnt++; if(cnt>Longest) Longest=cnt; } len[num[Lb]]=Longest; cnt=0; for(int i=l[num[Lb]];i<=r[num[Lb]];i++) if(a[i]) cnt++;//暴力sum1 sum1[num[Lb]]=cnt; cnt=0; for(int i=l[num[Lb]];i<=r[num[Lb]];i++) if(!a[i]) cnt++;//暴力sum0 sum0[num[Lb]]=cnt; } void makeblock() { memset(delta,-1,sizeof(delta)); sz=sqrt((double)n*0.5); for(sum=1;sum*sz<n;sum++) { l[sum]=(sum-1)*sz+1; r[sum]=sum*sz; for(int i=l[sum];i<=r[sum];i++) num[i]=sum; Work(l[sum],r[sum],-1); } l[sum]=sz*(sum-1)+1; r[sum]=n; for(int i=l[sum];i<=r[sum];i++) num[i]=sum; Work(l[sum],r[sum],-1); } inline void Update(const int &L,const int &R,const bool &sym) { Pushdown(num[L]); Pushdown(num[R]); if(num[L]==num[R]) Work(L,R,sym); else { Work(L,r[num[L]],sym); Work(l[num[R]],R,sym); for(int i=num[L]+1;i<num[R];i++) { if(Spin[i]) Spin[i]=false; delta[i]=sym; len[i]=lenl[i]=lenr[i]=sum1[i]=sym ? r[i]-l[i]+1 : 0; len0[i]=lenl0[i]=lenr0[i]=sum0[i]=sym ? 0 : r[i]-l[i]+1; } } } inline void Update_Spin(const int &L,const int &R) { Pushdown(num[L]); Pushdown(num[R]); if(num[L]==num[R]) Work(L,R,2); else { Work(L,r[num[L]],2); Work(l[num[R]],R,2); for(int i=num[L]+1;i<num[R];i++) { if(delta[i]==-1) Spin[i]^=1; else delta[i]^=1; swap(sum1[i],sum0[i]); swap(len[i],len0[i]); swap(lenl[i],lenl0[i]); swap(lenr[i],lenr0[i]); } } } inline void Query_Sum(const int &L,const int &R) { int ans=0; Pushdown(num[L]); Pushdown(num[R]); if(num[L]==num[R]) {for(int i=L;i<=R;i++) if(a[i]) ans++;} else { for(int i=L;i<=r[num[L]];i++) if(a[i]) ans++; for(int i=l[num[R]];i<=R;i++) if(a[i]) ans++; for(int i=num[L]+1;i<num[R];i++) ans+=sum1[i]; } P(ans); } 135 inline void Query_Len(const int &L,const int &R) 136 { 137 Pushdown(num[L]); 138 Pushdown(num[R]); 139 int ans=0,cnt=0; 140 if(num[L]==num[R]) 141 { 142 for(int i=L;i<=R;i++) 143 { 144 if(a[i]) cnt++; else cnt=0; 145 ans=max(ans,cnt); 146 } 147 P(ans); 148 } 149 else 150 { 151 int kua=0,cntr=0; 152 for(int i=r[num[L]];i>=L;i--) {if(!a[i]) break; kua++;} 153 for(int i=l[num[R]];i<=R;i++) {if(!a[i]) break; cntr++;} 154 for(int i=num[L]+1;i<num[R];i++) 155 { 156 if(kua) kua+=lenl[i]; 157 ans=max(ans,kua); 158 if(len[i]!=r[i]-l[i]+1) kua=0; 159 if(!kua&&lenr[i]) kua=lenr[i]; 160 ans=max(ans,len[i]); 161 } 162 for(int i=L;i<=r[num[L]];i++) 163 { 164 if(a[i]) cnt++; else cnt=0; 165 ans=max(ans,cnt); 166 } cnt=0; 167 for(int i=l[num[R]];i<=R;i++) 168 { 169 if(a[i]) cnt++; else cnt=0; 170 ans=max(ans,cnt); 171 } 172 P(max(ans,kua+cntr)); 173 } 174 } int main() { n=G();m=G(); for(int i=1;i<=n;i++) a[i]=G(); makeblock(); for(int i=1;i<=m;i++) { op=G();x=G();y=G();x++;y++; if(op==0) Update(x,y,0); else if(op==1) Update(x,y,1); else if(op==2) Update_Spin(x,y); else if(op==3) Query_Sum(x,y); else Query_Len(x,y); } return 0; }
相关文章推荐
- 【bzoj1858】【Scoi2010】序列操作【位运算】【卡常大法好】
- 【BZOJ1858】【SCOI2010】序列操作(线段树+合并)
- [BZOJ 1858][Scoi2010]序列操作(线段树)
- 【BZOJ1858】[Scoi2010]序列操作【线段树】
- bzoj 1858: [Scoi2010]序列操作 线段树
- bzoj 1858: [Scoi2010]序列操作(线段树)
- bzoj1858: [Scoi2010]序列操作
- BZOJ 1858 SCOI 2010 序列操作
- BZOJ 1858 SCOI2010 序列操作 线段树
- bzoj 1858: [Scoi2010]序列操作
- bzoj1858: [Scoi2010]序列操作
- bzoj1858【scoi2010】序列操作
- bzoj 1858: [Scoi2010] 序列操作 题解
- 【BZOJ1858】[Scoi2010]序列操作 线段树
- BZOJ1858: [Scoi2010]序列操作
- 【BZOJ 1858】 [Scoi2010]序列操作
- BZOJ 1858 [Scoi2010]序列操作 线段树
- BZOJ1858: [Scoi2010]序列操作
- bzoj 1858: [Scoi2010]序列操作 -- 线段树
- 【BZOJ 1858】【SCOI 2010】序列操作【区间线段树】