【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\n",(a[y/60]&(1ll<<(y%60)))?1:0);
        }
    }

    return 0;
}
posted @ 2018-05-17 08:06  Maxwei_wzj  阅读(116)  评论(0编辑  收藏  举报