題目大意
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;
}
}
}