【分块】bzoj1858 [Scoi2010]序列操作

分块 Or 线段树 分块的登峰造极之题

每块维护8个值:

包括左端点在内的最长1段;

包括右端点在内的最长1段;

该块内的最长1段;

该块内1的个数;

包括左端点在内的最长0段;//这四个是因为可能有翻转操作,需要swap 0有关的标记 和 1有关的标记

包括右端点在内的最长0段;

该块内的最长0段;

该块内0的个数。

2个懒标记:是否翻转,覆盖成了什么。

怎么处理一个块上有两个标记的情况呢?

若该块原来没有任何标记,或要打的标记和原本的标记种类相同,则直接打上标记;

若已有翻转标记,再覆盖时则先清除翻转标记,再打上覆盖标记;

若已有覆盖标记,再翻转时,则直接将覆盖标记取反。

So 某个块上同时只会有1个标记。

代码能力题,同样无法体现分块相对于线段树的“代码简介优势”,不推荐写。耗时也多于线段树。

P.S.注意取最长段的时候的细节……如下代码中高亮部分。

P.S.P.S.注意Pushdown的时机。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cmath>
  5 using namespace std;
  6 int len[400],lenl[400],lenr[400],n,m,l[400],r[400],num[100001],sum,sz,x,y;
  7 bool a[100001];
  8 int len0[400],lenl0[400],lenr0[400],sum1[400],sum0[400];
  9 int delta[400],op;
 10 bool Spin[400];
 11 int Res,Num;char C,CH[12];
 12 inline int G()
 13 {
 14     Res=0;C=‘*‘;
 15     while(C<‘0‘||C>‘9‘)C=getchar();
 16     while(C>=‘0‘&&C<=‘9‘){Res=Res*10+(C-‘0‘);C=getchar();}
 17     return Res;
 18 }
 19 inline void P(long long x)
 20 {
 21     Num=0;if(!x){putchar(‘0‘);puts("");return;}
 22     while(x>0)CH[++Num]=x%10,x/=10;
 23     while(Num)putchar(CH[Num--]+48);
 24     puts("");
 25 }
 26 void Pushdown(const int &p)
 27 {
 28     if(delta[p]!=-1)
 29       {
 30           for(int i=l[p];i<=r[p];i++) a[i]=delta[p];
 31           delta[p]=-1;
 32       }
 33     else if(Spin[p])
 34       {
 35           for(int i=l[p];i<=r[p];i++) a[i]^=1;
 36           Spin[p]=false;
 37       }
 38 }
 39 inline void Work(const int &Lb,const int &Rb,const int &sym)
 40 {
 41     if(sym==0||sym==1) {for(int i=Lb;i<=Rb;i++) a[i]=sym;}
 42     else if(sym==2) {for(int i=Lb;i<=Rb;i++) a[i]^=1;}
 43     int cnt=0;
 44     for(int i=l[num[Lb]];i<=r[num[Lb]];i++) {if(a[i]) break; cnt++;}//暴力lenl0
 45     lenl0[num[Lb]]=cnt; cnt=0;
 46     for(int i=r[num[Lb]];i>=l[num[Lb]];i--) {if(a[i]) break; cnt++;}//暴力lenr0
 47     lenr0[num[Lb]]=cnt; cnt=0;
 48     int Longest=0;
 49     for(int i=l[num[Lb]];i<=r[num[Lb]];i++)//暴力len0
 50       {
 51           if(a[i]) cnt=0; else cnt++;
 52           if(cnt>Longest) Longest=cnt;
 53       }
 54     len0[num[Lb]]=Longest; cnt=0;
 55     for(int i=l[num[Lb]];i<=r[num[Lb]];i++) {if(!a[i]) break; cnt++;}//暴力lenl
 56     lenl[num[Lb]]=cnt; cnt=0;
 57     for(int i=r[num[Lb]];i>=l[num[Lb]];i--) {if(!a[i]) break; cnt++;}//暴力lenr
 58     lenr[num[Lb]]=cnt; cnt=0; Longest=0;
 59     for(int i=l[num[Lb]];i<=r[num[Lb]];i++)//暴力len
 60       {
 61           if(!a[i]) cnt=0; else cnt++;
 62           if(cnt>Longest) Longest=cnt;
 63       }
 64     len[num[Lb]]=Longest; cnt=0;
 65     for(int i=l[num[Lb]];i<=r[num[Lb]];i++) if(a[i]) cnt++;//暴力sum1
 66     sum1[num[Lb]]=cnt; cnt=0;
 67     for(int i=l[num[Lb]];i<=r[num[Lb]];i++) if(!a[i]) cnt++;//暴力sum0
 68     sum0[num[Lb]]=cnt;
 69 }
 70 void makeblock()
 71 {
 72     memset(delta,-1,sizeof(delta));
 73     sz=sqrt(n);
 74     for(sum=1;sum*sz<n;sum++)
 75       {
 76         l[sum]=(sum-1)*sz+1;
 77         r[sum]=sum*sz;
 78         for(int i=l[sum];i<=r[sum];i++) num[i]=sum;
 79         Work(l[sum],r[sum],-1);
 80       }
 81     l[sum]=sz*(sum-1)+1; r[sum]=n;
 82     for(int i=l[sum];i<=r[sum];i++) num[i]=sum;
 83     Work(l[sum],r[sum],-1);
 84 }
 85 inline void Update(const int &L,const int &R,const bool &sym)
 86 {
 87     Pushdown(num[L]);
 88     Pushdown(num[R]);
 89     if(num[L]==num[R]) Work(L,R,sym);
 90     else
 91       {
 92           Work(L,r[num[L]],sym);
 93           Work(l[num[R]],R,sym);
 94           for(int i=num[L]+1;i<num[R];i++)
 95             {
 96                 if(Spin[i]) Spin[i]=false; delta[i]=sym;
 97                 len[i]=lenl[i]=lenr[i]=sum1[i]=sym ? r[i]-l[i]+1 : 0;
 98                 len0[i]=lenl0[i]=lenr0[i]=sum0[i]=sym ? 0 : r[i]-l[i]+1;
 99             }
100       }
101 }
102 inline void Update_Spin(const int &L,const int &R)
103 {
104     Pushdown(num[L]);
105     Pushdown(num[R]);
106     if(num[L]==num[R]) Work(L,R,2);
107     else
108       {
109           Work(L,r[num[L]],2);
110           Work(l[num[R]],R,2);
111           for(int i=num[L]+1;i<num[R];i++)
112             {
113                 if(delta[i]==-1) Spin[i]^=1; else delta[i]^=1;
114                 swap(sum1[i],sum0[i]);
115                 swap(len[i],len0[i]);
116                 swap(lenl[i],lenl0[i]);
117                 swap(lenr[i],lenr0[i]);
118             }
119       }
120 }
121 inline void Query_Sum(const int &L,const int &R)
122 {
123     int ans=0;
124     Pushdown(num[L]);
125     Pushdown(num[R]);
126     if(num[L]==num[R]) {for(int i=L;i<=R;i++) if(a[i]) ans++;}
127     else
128       {
129           for(int i=L;i<=r[num[L]];i++) if(a[i]) ans++;
130           for(int i=l[num[R]];i<=R;i++) if(a[i]) ans++;
131           for(int i=num[L]+1;i<num[R];i++) ans+=sum1[i];
132       }
133     P(ans);
134 }
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 }
175 int main()
176 {
177     n=G();m=G();
178     for(int i=1;i<=n;i++) a[i]=G();
179     makeblock();
180     for(int i=1;i<=m;i++)
181       {
182           op=G();x=G();y=G();x++;y++;
183           if(op==0) Update(x,y,0);
184           else if(op==1) Update(x,y,1);
185           else if(op==2) Update_Spin(x,y);
186           else if(op==3) Query_Sum(x,y);
187           else Query_Len(x,y);
188       }
189     return 0;
190 }
时间: 2024-10-10 03:22:00

【分块】bzoj1858 [Scoi2010]序列操作的相关文章

BZOJ1858[Scoi2010]序列操作 题解

题目大意: 有一个01序列,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0:1 a b 把[a, b]区间内的所有数全变成1:2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0:3 a b 询问[a, b]区间内总共有多少个1:4 a b 询问[a, b]区间内最多有多少个连续的1. 思路: 维护每一段数的和.左端和右端以及整段中连续的0和1的长度,并使用标记进行下传. 代码: 1 #include<cstdi

bzoj1858: [Scoi2010]序列操作

lazy-tag线段树. #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 800000 + 10; struct Segtree { #define lc(x) ((x)<<1) #define rc(x) (((x)<<1)|1) int l[maxn],r[maxn]; int l1[maxn],r1[ma

bzoj1858: [Scoi2010]序列操作 线段树

线段树,维护七个值两个标记. 用结构体快了一倍…… #include<bits/stdc++.h> #define N (1<<18) #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=0,int r=n-1,int k=1 using namespace

bzoj1858 [Scoi2010]序列操作——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1858 线段树...调了一个上午...(后面带 // 的都是改出来的) lazy 标记的下放好麻烦,还得考虑赋值和取反的先后顺序什么的... 因为在取反时把赋值标记 swap 了,所以下放的时候先判断取反再判断赋值... 而且WA了一上午的原因竟然是一开始不慎把取反以为成翻转了,后来没改干净...那个 rev 的名字啊... 总之没有太改变自己最初的想法.改了些细节就A了还是很高兴的! 代码

bzoj 1858: [Scoi2010] 序列操作 题解

[原题] 1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 1031  Solved: 529 [Submit][Status] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内

1858: [Scoi2010]序列操作

1858: [Scoi2010]序列操作 Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0 3 a b 询问[a, b]区间内总共有多少个1 4 a b 询问[a, b]区间内最多有多少个连续的1 对于每一

【BZOJ-1858】序列操作 线段树

1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1961  Solved: 991[Submit][Status][Discuss] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区

【BZOJ 1858】 [Scoi2010]序列操作

1858: [Scoi2010]序列操作 Time Limit: 10 Sec Memory Limit: 64 MB Submit: 1368 Solved: 712 [Submit][Status][Discuss] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区

bzoj 1858: [Scoi2010]序列操作

1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MB 线段树,对于每个区间需要分别维护左右中的1和0连续个数,并在op=4时特殊处理一下. Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内的所有数全