P3822 [NOI2017] 整数
https://www.luogu.com.cn/problem/P3822
由于每次加一后二进制数位变化的那个均摊性质,可以把加减法分开维护,每次分成 \(\log |a_i|\) 次给某个数位加一
然后如果进了位就暴力往后继续加一
这样还是有点超,就压位,用 unsigned long long
考虑怎么查询,先找到那个位置的加、减标记,异或一下,就得到了不考虑借位的答案
考虑什么时候会借位,查询位置前面的位置(位权更小的位置)的加减标记不能全部相同,设 \(o\) 为最靠后的一个在查询位置前、且不同的位置,那么如果这一位上加的标记小于减的标记,那就寄了,需要借位,那就把答案再异或上 \(1\)
就用 set
对于每个块(每六十四位一块)维护是否相同就行了
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
#include<assert.h>
#define EN puts("")
#define INT_INF ((int)0x3f3f3f3f)
#define LL_INF ((long long)0x3f3f3f3f3f3f3f3f)
inline long long read(){
register long long x=0;register int y=1;
register char c=getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
#define N 1000006
#define B 64
unsigned long long add[N*30/B+6],sub[N*30/B+6];
std::set<int>set;
inline void change(int b,unsigned long long *f,unsigned long long *g){
int block=b/B;b&=(B-1);
if(f[block]^g[block]) set.erase(block);
unsigned long long backup=f[block];
f[block]+=1ull<<b;
if(f[block]<backup) change((block+1)*B,f,g);
if(f[block]^g[block]) set.insert(block);
}
inline void change(int a,int b){
unsigned long long *f=add,*g=sub;
if(a<0) a=-a,std::swap(f,g);
for(int i=0;(1ll<<i)<=a;i++)if((1<<i)&a) change(b+i,f,g);
}
inline int ask(int k){
int block=k/B;k&=(B-1);
int ans=((add[block]^sub[block])>>k)&1;
unsigned long long p=add[block]&((1ull<<k)-1),q=sub[block]&((1ull<<k)-1);
if(p<q) ans^=1;
else if(p==q&&!set.empty()&&block>*(set.begin())){
int o=*(--set.lower_bound(block));
ans^=(add[o]<=sub[o]);
}
return ans;
}
int main(){
int n=read();read();read();read();
while(n--){
int op=read(),a=read();
if(op==1) change(a,read());
else printf("%d\n",ask(a));
}
return 0;
}