题解[P5312竞赛实验班]

题目链接

题意:维护一个序列支持:加一个数、整体异或、整体排序、求区间和。

\(\text{Step 1}\)

先考虑没有加入一个数该怎么做。

由于异或与排序都是基于整体的,记录最近一次排序时整体异或上了 \(s\) ,以及当前序列整体异或上了 \(t\)

那查询区间 \([l,r]\) 的和就相当于查询序列中异或上 \(s\) 后,前 \(r\) 小的值异或上 \(t\) 之和,减去前 \(l-1\) 小的值异或上 \(t\) 之和。

所以现在要维护的就是:异或 \(s\) 后前 \(k\) 小的异或上 \(t\) 的和。

那就可以用 \(\text{01trie}\) 去维护异或 \(s\) 后前 \(k\) 小,然后在 \(\text{01trie}\) 上拆位,记录异或 \(s\) 后前 \(k\) 小的数在每一位上有多少个 \(1\) ,第 \(j\) 位上 \(1\) 的个数记为 \(sum_j\)

再对 \(t\) 拆位统计贡献,如果 \(t\) 在第 \(j\) 位上为 \(0\) ,那异或后会产生 \(sum_j\times 2^j\) 的贡献,若 \(t\) 在第 \(j\) 位上为 \(1\) ,那异或后会产生 \((k-sum_j)\times2^j\) 的贡献。

这样时空复杂度都是 \(O(n\log^2 n)\) 的,但丝毫不卡。

\(\text{Step 2}\)

再考虑有加数该如何实现。

加数会使得序列中一部分是无需段,但有序段仍可以用上述方法。

只需在排序是要把 把无序段中的数异或上 它们加进序列时整个序列异或的值。

这是为了保证查询时这些数仍是基于整体的 “ 异或 \(s\) 后前 \(k\) 小的异或上 \(t\) 的和 ”。

而对于无序段,当然可以拆位建线段树,在整体异或时打区间 \(01\) 互换的标记。

但还有更快的一个 \(\log\) 的做法:

仍然让这些值在加进来时,异或上当前序列异或的值,保证查时是基于整体的异或上 \(t\)

然后对每一位用前缀和记录前 \(i\) 个数在第 \(j\) 位上 \(1\) 的个数 \(f_i^j\)

那查一个无序段的区间 \([l,r]\) 之和时,同样对 \(t\) 拆位,如果 \(t\) 在第 \(j\) 位上为 \(0\) ,那异或后会产生 \((f_r^j-f_{l-1}^j)\times 2^j\) 的贡献,若 \(t\) 在第 \(j\) 位上为 \(1\) ,那异或后会产生 \((r-l+1-(f_r^j-f_{l-1}^r))\times2^j\) 的贡献。

那查询时按无序段与有序段分开统计贡献就行了。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10,K=31;
int n,m,x,y,xx,yy,s,ss,last,opt,rt,tot;
ll ans;int a[N];char ch;
int tmp[K+1];
inline void read(int &x){
	x=0;ch=getchar();
	while(ch<48||ch>57)ch=getchar();
	while(ch>47&&ch<58)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
void write(ll x){if(x>9)write(x/10);putchar(48+x%10);}
#define lowbit(i) i&(-i)
int sum[K+1][N];bool c[K+1];
struct trie{int son[2],s,t[K+1];}t[N*(K+1)];
void update(int &k,int x,int i){
	if(!k)k=++tot;++t[k].s;
	for(int j=K;~j;--j)t[k].t[j]+=c[j];
	if(~i)update(t[k].son[(x>>i)&1],x,i-1);
}
void inquiry(int k,int x,int kth,int i){
	if(i<0)for(int j=K;~j;--j)tmp[j]+=min(t[k].t[j],kth);
	else {
		bool b=(x>>i)&1;int ksb=t[k].son[b];
		if(t[ksb].s>=kth)inquiry(ksb,x,kth,i-1);
		else {
			for(int j=K;~j;--j)tmp[j]+=t[ksb].t[j];
			inquiry(t[k].son[!b],x,kth-t[ksb].s,i-1);
		}
	}
}
main(){
	read(n);register int i,j;
	last=1;
	for(i=1;i<=n;++i){
		read(x);a[i]=x;
		for(j=K;~j;--j)sum[j][i]=sum[j][i-1]+((x>>j)&1);
	}
	read(m);
	while(m--){
		read(opt);
		if(opt==1){
			++n;read(x);x^=s;a[n]=x;
			for(j=K;~j;--j)sum[j][n]=sum[j][n-1]+((x>>j)&1);
		}
		else if(opt==3)read(x),s^=x;
		else if(opt==4){
			ss=s;
			for(;last<=n;++last){
				x=a[last];
				for(j=K;~j;--j)c[j]=(x>>j)&1;
				update(rt,x,K);
			}
		}
		else {
			read(x),read(y);
			ans=0;
			if(y>=last){
				xx=max(x,last)-1;
				for(j=K;~j;--j){
					if((s>>j)&1)ans+=(1ll<<j)*(y-sum[j][y]-xx+sum[j][xx]);
					else ans+=(1ll<<j)*(sum[j][y]-sum[j][xx]);
				}
			}
			if(x<last){
				yy=min(y,last-1);
				inquiry(rt,ss,yy,K);
				for(j=K;~j;--j){
					if((s>>j)&1)ans+=(1ll<<j)*(yy-tmp[j]);
					else ans+=(1ll<<j)*tmp[j];
					tmp[j]=0;
				}
				inquiry(rt,ss,x-1,K);
				for(j=K;~j;--j){
					if((s>>j)&1)ans-=(1ll<<j)*(x-1-tmp[j]);
					else ans-=(1ll<<j)*tmp[j];
					tmp[j]=0;
				}
			}
			write(ans);putchar('\n');
		}
	}
}
posted @ 2021-10-03 18:23  Y_B_X  阅读(25)  评论(0编辑  收藏  举报