LOJ2302 - 「NOI2017」整數
阿新 • • 發佈:2018-04-04
pri urn pic blog () def string 有一個 https
Portal
Description
有一個整數\(x=0\),對其進行\(n(n\leq10^6)\)次操作:
- 給出\(a(|a|\leq10^9),b(b\leq30n)\),將\(x\)加上\(a\cdot 2^b\)。
- 詢問\(x\)在二進制下位權為\(2^k(k\leq30n)\)的位的值。
保證任意時刻\(x\geq0\)。
Solution
用線段樹來模擬二進制下的加減運算。
線段樹上的每個位置維護\(30\)位二進制數,即第一位維護\(2^0...2^{29}\),第二位維護\(2^{30}...2^{59}\),以此類推。考慮當我們將某位置上的值修改為\(a\)後應當怎麽做:
- 若\(a\in[0,2^{30})\)
- 若\(a\geq2^{30}\),則需要進位:將前面一段連續的\(2^{30}-1\)修改為\(0\),然後將這段\(2^{30}-1\)前面的一個數\(+1\)。
- 若\(a<0\),則需要退位:將前面一段連續的\(0\)修改為\(2^{30}-1\),然後將這段\(0\)前面的一個數\(-1\)。
那麽我們需要區間修改,單點查詢,並維護每個區間從低位到高位連續的\(0/2^{30}-1\)的長度。具體細節可以看代碼。
時間復雜度\(O(nlogn)\)。
Code
//「NOI2017」整數
#include <cstdio>
#include <cstring>
inline char gc()
{
static char now[1<<16],*s,*t;
if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
return *s++;
}
inline int read()
{
int x=0,f=1; char ch=gc();
while(ch<'0'||'9'<ch) f=(ch^'-')?f:-1,ch=gc();
while('0' <=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int const N=4e6+10;
int const N0=30;
int n=1e6+100;
int Q,t1,t2,t3;
#define Ls (p<<1)
#define Rs ((p<<1)|1)
int rt,len[2][N],val[N],tag[N];
void update(int p,int L0,int R0)
{
int mid=L0+R0>>1;
len[0][p]=len[0][Ls]+(len[0][Ls]==mid-L0+1)*len[0][Rs];
len[1][p]=len[1][Ls]+(len[1][Ls]==mid-L0+1)*len[1][Rs];
}
void change(int p,int L0,int R0,int x)
{
if(x) val[p]=(1<<N0)-1; else val[p]=0;
tag[p]=x,len[x][p]=R0-L0+1,len[x^1][p]=0;
}
void pushdw(int p,int L0,int R0)
{
if(tag[p]<0) return;
int mid=L0+R0>>1;
change(Ls,L0,mid,tag[p]),change(Rs,mid+1,R0,tag[p]);
tag[p]=-1;
}
int L,R,cyV;
void ins1(int p,int L0,int R0,int v)
{
if(L<=L0&&R0<=R)
{
val[p]+=v;
if(val[p]>=(1<<N0)) cyV=1,val[p]&=(1<<N0)-1;
if(val[p]<0) cyV=0,val[p]+=1<<N0;
len[0][p]=len[1][p]=0;
if(val[p]==0) len[0][p]=1;
if(val[p]==(1<<N0)-1) len[1][p]=1;
return;
}
pushdw(p,L0,R0);
int mid=L0+R0>>1;
if(L<=mid) ins1(Ls,L0,mid,v);
if(mid<R) ins1(Rs,mid+1,R0,v);
update(p,L0,R0);
}
void ins2(int p,int L0,int R0,int v)
{
if(L<=L0&&R0<=R) {change(p,L0,R0,v); return;}
pushdw(p,L0,R0);
int mid=L0+R0>>1;
if(L<=mid) ins2(Ls,L0,mid,v);
if(mid<R) ins2(Rs,mid+1,R0,v);
update(p,L0,R0);
}
int query1(int p,int L0,int R0)
{
if(L<=L0&&R0<=R) return val[p];
pushdw(p,L0,R0);
int mid=L0+R0>>1;
if(L<=mid) return query1(Ls,L0,mid);
if(mid<R) return query1(Rs,mid+1,R0);
}
//query2返回在[L0,R0]∩[L,R]中L向後有多少個連續的v
int query2(int p,int L0,int R0,int v)
{
if(L<=L0&&R0<=R) return len[v][p];
pushdw(p,L0,R0);
int mid=L0+R0>>1,r1=0,r2=0;
if(L<=mid) r1=query2(Ls,L0,mid,v);
if(mid<R) r2=query2(Rs,mid+1,R0,v);
if(L<=mid) return r1+(r1==mid-L+1)*r2;
else return r2;
}
void add(int x,int v)
{
L=R=x,cyV=-1; ins1(rt,1,n,v);
if(cyV>=0)
{
L=x+1,R=n; int len=query2(rt,1,n,cyV);
L=x+1,R=x+len; if(L<=R) ins2(rt,1,n,cyV^1);
L=R=x+len+1; ins1(rt,1,n,cyV?1:-1);
}
}
int main()
{
Q=read(); t1=read(),t2=read(),t3=read();
memset(tag,-1,sizeof tag);
rt=1;
change(rt,1,n,0);
for(int i=1;i<=Q;i++)
{
int opt=read();
if(opt==1)
{
int a=read(),b=read();
int b1=b/N0+1,b2=b%N0,t=1<<(N0-b2);
int a1=a/t,a2=a%t*(1<<b2);
add(b1,a2); add(b1+1,a1);
}
else
{
int k=read();
int k1=k/N0+1,k2=k%N0;
L=R=k1; printf("%d\n",(query1(rt,1,n)>>k2)&1);
}
}
return 0;
}
P.S.
多謝Pickupwin大佬指點
LOJ2302 - 「NOI2017」整數