zoukankan      html  css  js  c++  java
  • 【BZOJ4942】整数(NOI2017)-线段树+压位

    测试地址:整数
    做法:本题需要用到线段树+压位。
    首先考虑在某一位加1或减1的情况。在加1时,我们要从当前位开始,找到最低的为0的位,然后把这一位加1,路上经过的所有位都清零。在减1时,我们要从当前位开始,找到最低的为1的位,然后把这一位减1,路上经过的所有位都修改成1。这些操作显然可以在线段树上完成。
    但是我们发现,操作的数位的范围可能特别大,达到3×107O(nlogn)的时间复杂度不能承受。那么我们可以把30个二进制位压成一位,或者甚至把60个二进制位压成一位,然后在操作的时候,原来的找0就变成了找第一个不全是1的段,原来的找1就变成了找第一个不全是0的段,那么压位后一次修改最多涉及两次操作,常数大大降低,就可以通过此题了。
    我傻逼的地方:某个函数中没有下放标记……调的我好苦啊……
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,t1,t2,t3;
    ll a[1000010]={0};
    bool flag[4000010][2];
    
    void buildtree(int no,int l,int r)
    {
        flag[no][0]=1;
        flag[no][1]=0;
        if (l==r) return;
        int mid=(l+r)>>1;
        buildtree(no<<1,l,mid);
        buildtree(no<<1|1,mid+1,r);
    }
    
    void pushdown(int no)
    {
        if (flag[no][0])
        {
            flag[no<<1][0]=flag[no<<1|1][0]=1;
            flag[no<<1][1]=flag[no<<1|1][1]=0;
        }
        if (flag[no][1])
        {
            flag[no<<1][0]=flag[no<<1|1][0]=0;
            flag[no<<1][1]=flag[no<<1|1][1]=1;
        }
    }
    
    void pushup(int no)
    {
        flag[no][0]=flag[no<<1][0]&flag[no<<1|1][0];
        flag[no][1]=flag[no<<1][1]&flag[no<<1|1][1];
    }
    
    void pushdownto(int no,int l,int r,int x)
    {
        if (x>n/2+1) return;
        if (l==r)
        {
            if (flag[no][1]) a[l]=(1ll<<60)-1ll;
            if (flag[no][0]) a[l]=0;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (x<=mid) pushdownto(no<<1,l,mid,x);
        else pushdownto(no<<1|1,mid+1,r,x);
    }
    
    void modify(int no,int l,int r,int s,int t,bool type)
    {
        if (s>t) return;
        if (l>=s&&r<=t)
        {
            flag[no][type]=1;
            flag[no][!type]=0;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (s<=mid) modify(no<<1,l,mid,s,t,type);
        if (t>mid) modify(no<<1|1,mid+1,r,s,t,type);
        pushup(no);
    }
    
    void add(int no,int l,int r,int x,ll val)
    {
        if (x>n/2+1) return;
        if (l==r)
        {
            if (flag[no][0]) a[l]=0;
            if (flag[no][1]) a[l]=(1ll<<60)-1ll; //就是这个地方忘记下放
            a[l]+=val;
            flag[no][0]=(a[l]==0);
            flag[no][1]=(a[l]==((1ll<<60)-1ll));
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (x<=mid) add(no<<1,l,mid,x,val);
        else add(no<<1|1,mid+1,r,x,val);
        pushup(no);
    }
    
    int find(int no,int l,int r,int x,bool type)
    {
        if (x>=n/2+1) return -1;
        if (flag[no][type]) return -1;
        if (l==r) return l;
        int mid=(l+r)>>1,ans=-1;
        pushdown(no);
        if (x<mid) ans=find(no<<1,l,mid,x,type);
        if (ans!=-1) return ans;
        ans=find(no<<1|1,mid+1,r,x,type);
        return ans;
    }
    
    void addnumber(int c,ll d)
    {
        if (c>n/2+1) return;
        pushdownto(1,0,n/2+1,c);
        if (a[c]+d<(1ll<<60)) add(1,0,n/2+1,c,d);
        else
        {
            add(1,0,n/2+1,c,d-(1ll<<60));
            int pos=find(1,0,n/2+1,c,1);
            if (pos==-1) pos=n/2+2;
            modify(1,0,n/2+1,c+1,pos-1,0);
            add(1,0,n/2+1,pos,1ll);
        }
    }
    
    void subnumber(int c,ll d)
    {
        if (c>n/2+1) return;
        pushdownto(1,0,n/2+1,c);
        if (a[c]-d>=0) add(1,0,n/2+1,c,-d);
        else
        {
            add(1,0,n/2+1,c,(1ll<<60)-d);
            int pos=find(1,0,n/2+1,c,0);
            if (pos==-1) pos=n/2+2;
            modify(1,0,n/2+1,c+1,pos-1,1);
            add(1,0,n/2+1,pos,-1ll);
        }
    }
    
    int main()
    {
        scanf("%d%d%d%d",&n,&t1,&t2,&t3);
        buildtree(1,0,n/2+1);
        for(int i=1;i<=n;i++)
        {
            int op,y;
            ll x;
            scanf("%d",&op);
            if (op==1)
            {
                scanf("%lld%d",&x,&y);
                int c=y/60,d=y%60;
                if (x<0)
                {
                    x=-x;
                    if (x%(1ll<<(60-d))) subnumber(c,x%(1ll<<(60-d))*(1ll<<d));
                    if (x>>(60-d)) subnumber(c+1,x>>(60-d));
                }
                else
                {
                    if (x%(1ll<<(60-d))) addnumber(c,x%(1ll<<(60-d))*(1ll<<d));
                    if (x>>(60-d)) addnumber(c+1,x>>(60-d));
                }
            }
            else
            {
                scanf("%d",&y);
                pushdownto(1,0,n/2+1,y/60);
                printf("%d
    ",(a[y/60]&(1ll<<(y%60)))?1:0);
            }
        }
    
        return 0;
    }
  • 相关阅读:
    随笔—邀请赛前训— Codeforces Round #330 (Div. 2) B题
    随笔—邀请赛前训— Codeforces Round #330 (Div. 2) Vitaly and Night
    General Problem Solving Techniques [Examples]~C
    General Problem Solving Techniques [Examples]~A
    General Problem Solving Techniques [Beginner-1]~B
    General Problem Solving Techniques [Beginner-1]~A
    General Problem Solving Techniques [Beginner-1]~E
    General Problem Solving Techniques [Beginner-1]~F
    2015 HUAS Summer Contest#5~C
    2015 HUAS Summer Contest#5~B
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793373.html
Copyright © 2011-2022 走看看