[SHOI2015]脳洞治療機器

[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;
}

おすすめ

転載: www.cnblogs.com/wawawa8/p/11113578.html