题目大意
n次操作维护一个长度为n的二进制整数x,初始为丰,支持以下两种操作:
1.将这个整数加上ai ·
2. 询问这个整数二进制意义下第ki位的值。
n<=1000000 <=1e9 b<=30
题目解法
注意高位进行加法不会对低位产生影响
维护每一位的值,并在线段树上记录每个区间是否含有0或1,以便发生进退位时快速查找到进退位结束的位置。
先说 的做法:
由于 |a| 只有 1e9 ,因此把 a分解成 ∑ 的形式,就变成了 logn 次加/减 。
考虑加法,如果这一位是0则直接改为1,否则进位,相当于这一位变成0,下一位+1。最终结果就是找到这一位下面第一个为0的位,把该位+1,[当前位,该位)变成0。
减法同理,如果是1则直接该为0,否则退位,找到这一位下面第一个为1的为,把该位-1,[当前位,该位)变成1。
使用线段树维护区间是否全为0/1,单次加减的时间复杂度就是 logn ,n次 logn次加减,时间复杂度 O(nlog2n),这样只有68分。
考虑优化:维护01信息过于浪费,考虑压位,维护每连续30位的数是什么。这样一次操作最多给两个位加减,加法就判断这一位加了以后是否超过-1 ,超过就找下面第一个不为−1 的位,把该位+1,(当前位,该位)变成0,当前位直接变成进位后的结果。减法同理。//(合理运用二进制性质)(注意当处理+1、-1时需特判,快速找到下一个0/1减少时间复杂度到真正意义上o(nlogn))
其中维护一段区间是否全 0/全-1 可以通过维护区间或和区间与来解决。
事实上,由于a在线段树中最多对应串个叶子结点,而叶子结点内部的进位就是整数加减法,不需要专门
在线段树上查找。我们可以直接将a 拆分成两次修改,时间复杂度o(n log n)
程序常数不怎么优秀(望给予指导)
#include<bits/stdc++.h>
using namespace std;
const int N=3000000;
const long long base=30,all=(1<<30)-1;
long long n,opt,m,a,b,pos,zhi,tmp;
long long mark[N],val[N];
int read(){
int x=0,f=1;
char c=getchar();
while((c<'0')||(c>'9')){if(c=='-')f=-1;c=getchar();}
while((c>='0')&&(c<='9'))x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
void update(int x)
{
if (mark[x]==1) { mark[x*2]=1; mark[x*2+1]=1;val[x*2]=all; val[x*2+1]=all;}
if (mark[x]==0) { mark[x*2]=0; mark[x*2+1]=0;val[x*2]=0; val[x*2+1]=0;}
}
int add(int a1,int l,int r,int pos,int zhi)
{
int res=0;
if (zhi==0) return 0;
if (l==r) {if (all-val[a1]<zhi)
{val[a1]=zhi+val[a1]-all-1;
if (val[a1]==0) mark[a1]=0; else mark[a1]=-1; return 1;}
else {val[a1]=val[a1]+zhi;
if (val[a1]==all) mark[a1]=1; else mark[a1]=-1; return 0;}}
int mid=(l+r)/2;
update(a1);
if((zhi==1)&&(mark[a1]==1)&&(pos==l)){
mark[a1]=0;
return 1;}
if (pos<=mid) { res=add(a1*2,l,mid,pos,zhi);
if (res) res=add(a1*2+1,mid+1,r,mid+1,1);
}
else res=add(a1*2+1,mid+1,r,pos,zhi);
mark[a1]=(mark[a1*2]==mark[a1*2+1])? mark[a1*2]:-1;
return res;
}
int del(int a1,int l,int r,int pos,int zhi)
{ int res=0;
if (zhi==0) return 0;
if (l==r) {if (val[a1]<zhi) { val[a1]=all+1+val[a1]-zhi;
if (val[a1]==all) mark[a1]=1;
else mark[a1]=-1; return 1;}
else { val[a1]=val[a1]-zhi;
if (val[a1]==0) mark[a1]=0;
else mark[a1]=-1; return 0;}}
int mid=(l+r)/2;
update(a1);
if((zhi==1)&&(mark[a1]==0)&&(pos==l)){
mark[a1]=1;
return 1;
}
if (pos<=mid) { res=del(a1*2,l,mid,pos,zhi);
if (res) res=del(a1*2+1,mid+1,r,mid+1,1);}
else res=del(a1*2+1,mid+1,r,pos,zhi);
mark[a1]=(mark[a1*2]==mark[a1*2+1])? mark[a1*2]:-1;
return res;
}
long long query(int a1,int l,int r,int pos)
{ long long res;
if (l==r) return val[a1];
update(a1);
int mid=(l+r)/2;
if (pos<=mid) res=query(a1*2,l,mid,pos); else res=query(a1*2+1,mid+1,r,pos);
mark[a1]=(mark[a1*2]==mark[a1*2+1])? mark[a1*2]:-1;
return res;
}
int main()
{
n=read(); m=read(); m=read(); m=read(); m=n;
memset(val,0,sizeof(val));
for (int i=1;i<=n;i++)
{ opt=read();
if (opt==1) {a=read(); b=read();
pos=b/base; tmp=b%base;
if (a>0){ add(1,0,n,pos,(a&((1<<(base-tmp))-1))<<tmp);
add(1,0,n,pos+1,a>>(base-tmp));}
else
{ a=-a; pos=b/base; tmp=b%base;
del(1,0,n,pos,(a&((1<<(base-tmp))-1) )<<tmp);
del(1,0,n,pos+1,a>>(base-tmp));
}
}
else
{ b=read();
pos=b/base;
zhi=query(1,0,n,pos);
cout<<(zhi>>(b-pos*base)&1)<<endl;
}
}
}