[SHOI2015]脑洞治疗仪
[题目链接]
[思路要点]
较为容易
线段树维护区间的左边一段连续 (0) 的数量,右边一段 (0) 的数量,除了两边两段 (0) 以外的连续的 (0) 的数量最大值,该区间 (0) 的总数以及该区间是否全部为 (0)
然后更新很显然,每个都讨论一下
对于 (0) 操作,就是一次区间赋值
对于 (1) 操作,首先找到 ([l_0,r_0]) 区间内的 (1) 的个数,然后在 ([l_1,r_1]) 区间内线段树二分,找到某个前缀的 (0) 的数量正好是之前 (1) 的个数的位置,然后区间赋值成 (1) 即可
对于 (2) 操作,显然的区间询问,注意两边的处理即可
[代码]
#include<cstdio>
#include<iostream>
#ifdef ONLINE_JUDGE
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){if(A==B){B=(A=ss)+fread(ss,1,1<<17,stdin);if(A==B)return EOF;}return*A++;}
template<class T>inline void read(T&x){
static char c;static int y;
for(c=gc(),x=0,y=1;c<48||57<c;c=gc())if(c=='-')y=-1;
for(;48<=c&&c<=57;c=gc())x=((x+(x<<2))<<1)+(c^'0');
x*=y;
}
#else
void read(int &x){scanf("%d",&x);}
#endif //快读
using namespace std;
int ad[800001],sum[800001],n,t;
inline void write(int x){if(x>9) write(x/10);putchar(x%10^48);}
inline void pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int rt,int l,int r){ //菜鸡专属建树
if (l==r){
sum[rt]=1;
return;
}
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt);
}
inline void pushdown(int rt,int l,int r){
if (ad[rt]){
int m=(l+r)>>1;
ad[rt]--;
sum[rt<<1]=ad[rt]*(m-l+1);
sum[rt<<1|1]=ad[rt]*(r-m);
ad[rt<<1]=ad[rt]+1;
ad[rt<<1|1]=ad[rt]+1;
ad[rt]=0;
}
}
void update(int rt,int l,int r,int x,int y,int k){ //区间都附成k
if (l>y||x>r) return;
if (x<=l&&r<=y){
ad[rt]=k+1; //由于有0,k+1方便一些
sum[rt]=k*(r-l+1);
return;
}
pushdown(rt,l,r);
int m=(l+r)>>1;
if (m>=x) update(rt<<1,l,m,x,y,k);
if (m<y) update(rt<<1|1,m+1,r,x,y,k);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y){ //查询区间和
if (l>y||x>r) return 0;
if (x<=l&&r<=y) return sum[rt];
pushdown(rt,l,r);
int m=(l+r)>>1,ret=0;
if (m>=x) ret+=query(rt<<1,l,m,x,y);
if (m<y) ret+=query(rt<<1|1,m+1,r,x,y);
return ret;
}
inline int ef(int x,int y,int k){ //二分查找x到y之间连续最长的k序列
int l=0,r=y-x,mid;
if (x>y) return -1;
while (l<=r){
mid=(l+r)>>1;
if (query(1,1,n,x,x+mid)==k*(mid+1)) l=mid+1;
else r=mid-1;
}
return l;
}
int main(){
int x,y,b,c,d;
read(n); read(t);
build(1,1,n);
while (t--){
read(b),read(x),read(y);
if (b==0){
update(1,1,n,x,y,0);
}
if (b==1){
read(c),read(d);
int num=query(1,1,n,x,y),p=0;
update(1,1,n,x,y,0);
if (num>=(d-c+1)-query(1,1,n,c,d)){
update(1,1,n,c,d,1); continue;
}
if (num==0) continue;//以上两个特判为菜鸡T了之后无助的挣扎
while (1){
if (query(1,1,n,c,c)==1){ //开头为一
p=ef(c,d,1); c+=p;
}
p=ef(c,d,0);
if (p==-1) break;
if (p>num){
update(1,1,n,c,c+num-1,1);
break;
}
else{
update(1,1,n,c,c+p-1,1);
num-=p; c+=p;
}
}
}
if (b==2){
int ans=0,p=0;
while (1){
if (query(1,1,n,x,x)==1){
p=ef(x,y,1); x+=p;
}
p=ef(x,y,0);
if (p==-1) break;
ans=max(ans,p);
x+=p; if (x>y) break; //忘写这个85分死循环
}
write(ans); puts("");
}
}
return 0;
}