题目描述

传送门

解法

奇妙的拼盘题。

首先考虑没有操作 \(1,4\) 我们可以怎么做。将数字进行拆位,这样可以按位做一个 \(0,1\) 的前缀和。这是 \(\mathcal O(n\log v)\) 的。

如果加上操作 \(4\) 呢?可以利用 \(\rm trie\) 树,在插入的时候就会帮你排好序。具体而言,先将所有数字的原始值插入 \(\rm trie\) 树。一次查询 \((l,r)\) 可以将其差分一下,变成查询当前数列前 \(x\) 个数的和。假设在某次排序之前所有操作 \(3\) 的异或和为 \(val\),并维护所有操作 \(3\) 的异或和 \(all\)。可以发现,某个数在数列中的位置和初始值、\(val\) 有关。在遍历 \(\rm trie\) 树时,我们不能认为 \(0\) 方向比 \(1\) 方向小,而是认为 \(val\) 在这一位的方向比另一位的方向更小。所以在每次排序后,我们都需要处理出 \(rev_i\) 表示每一位到底哪个方向更小。这是 \(\mathcal O(n\log^2 v)\) 的。

再加上操作 \(1\)?因为序列由 "有序 + 无序" 构成,在下一次排序之前,我们将操作 \(1\) 加入的数做最前面的前缀和,排序时插入即可。注意加入的数要异或上当前的 \(all\)

总时间复杂度 \(\mathcal O(n\log^2 v)\)

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

typedef long long ll;

const int maxn=1e5+5;

int n,a[maxn<<1],all,t[maxn*62][2],idx;
int cur,siz[maxn*62],cnt[maxn*62][31];
int pre[maxn<<1][31],rev[31];

void ins(int x) {
	int p=0;
	for(int i=30;i>=0;--i) {
		bool d=(x>>i&1);
		t[p][d]=(t[p][d]?t[p][d]:++idx);
		p=t[p][d];
		++siz[p];
		for(int j=0;j<=30;++j)
			cnt[p][j]+=(x>>j&1);
	}
}

ll ask(int l,int r) {
	ll ret=0;
	for(int i=0;i<=30;++i)
		ret+=(ll)((all>>i&1)?r-l+1-pre[r][i]+pre[l-1][i]:pre[r][i]-pre[l-1][i])<<i;
	return ret;
}

ll Ask(int p) {
	ll ret=0; int o=0,val=0;
	for(int i=30;i>=0;--i) {
		if(p<=siz[t[o][rev[i]]]) {
			val^=(rev[i]<<i);
			o=t[o][rev[i]];
		}
		else {
			int id=t[o][rev[i]];
			p-=siz[id];
			for(int j=0;j<=30;++j)
				ret+=(ll)((all>>j&1)?siz[id]-cnt[id][j]:cnt[id][j])<<j;
			val^=((rev[i]^1)<<i);
			o=t[o][rev[i]^1];
		}
	}
	return ret+1ll*(val^all)*p;
}

ll query(int l,int r) {
	if(l>cur) 
		return ask(l,r);
	if(r<=cur)
		return Ask(r)-Ask(l-1);
	return Ask(cur)-Ask(l-1)+ask(cur+1,r);
}

signed main() {
	n=read(9);
	for(int i=1;i<=n;++i) {
		a[i]=read(9);
		for(int j=0;j<=30;++j)
			pre[i][j]=pre[i-1][j]+(a[i]>>j&1);
	}
	int op,x,y;
	for(int m=read(9);m;--m) {
		op=read(9);
		if(op==1) {
			a[++n]=read(9)^all;
			for(int i=0;i<=30;++i)
				pre[n][i]=pre[n-1][i]+(a[n]>>i&1);
		}
		else if(op==2) {
			x=read(9),y=read(9);
			print(query(x,y),'\n');
		}
		else if(op==3) all^=read(9);
		else {
			while(cur<n)
				ins(a[++cur]);
			for(int i=0;i<=30;++i)
				pre[n][i]=0,
				rev[i]=(all>>i&1);
		}
	}
	return 0;
}
posted on 2021-09-03 14:57  Oxide  阅读(54)  评论(0编辑  收藏  举报