题目描述
解法
奇妙的拼盘题。
首先考虑没有操作 (1,4) 我们可以怎么做。将数字进行拆位,这样可以按位做一个 (0,1) 的前缀和。这是 (mathcal O(nlog v)) 的。
如果加上操作 (4) 呢?可以利用 ( m trie) 树,在插入的时候就会帮你排好序。具体而言,先将所有数字的原始值插入 ( m trie) 树。一次查询 ((l,r)) 可以将其差分一下,变成查询当前数列前 (x) 个数的和。假设在某次排序之前所有操作 (3) 的异或和为 (val),并维护所有操作 (3) 的异或和 (all)。可以发现,某个数在数列中的位置和初始值、(val) 有关。在遍历 ( m trie) 树时,我们不能认为 (0) 方向比 (1) 方向小,而是认为 (val) 在这一位的方向比另一位的方向更小。所以在每次排序后,我们都需要处理出 (rev_i) 表示每一位到底哪个方向更小。这是 (mathcal O(nlog^2 v)) 的。
再加上操作 (1)?因为序列由 "有序 + 无序" 构成,在下一次排序之前,我们将操作 (1) 加入的数做最前面的前缀和,排序时插入即可。注意加入的数要异或上当前的 (all)。
总时间复杂度 (mathcal O(nlog^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),'
');
}
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;
}