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