P3822 [NOI2017] 整数


https://www.luogu.com.cn/problem/P3822

由于每次加一后二进制数位变化的那个均摊性质,可以把加减法分开维护,每次分成 \(\log |a_i|\) 次给某个数位加一
然后如果进了位就暴力往后继续加一
这样还是有点超,就压位,用 unsigned long long

考虑怎么查询,先找到那个位置的加、减标记,异或一下,就得到了不考虑借位的答案
考虑什么时候会借位,查询位置前面的位置(位权更小的位置)的加减标记不能全部相同,设 \(o\) 为最靠后的一个在查询位置前、且不同的位置,那么如果这一位上加的标记小于减的标记,那就寄了,需要借位,那就把答案再异或上 \(1\)

就用 set 对于每个块(每六十四位一块)维护是否相同就行了

#include
#include
#include
#include
#include
#include
#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::setset;
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<>k)&1;
	unsigned long long p=add[block]&((1ull<*(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;
}

相关