suxxsfe

一言(ヒトコト)

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;
}
posted @ 2022-06-23 19:50  suxxsfe  阅读(47)  评论(0编辑  收藏  举报