题目:
分析:
操作4比较特殊,我们先来分析下操作4
操作4相当于需要一个数据结构,使得里面的数据有序(这有很多选择)
结合操作1,操作4的“排序”实际上指的是,将上一次排序之后加入的一些点,插入到这个数据结构中,ok,这也很中规中矩
于是我们需要一个数据结构和一个数组,数据结构存着有序的情况,数组存着后来插入的数,如果遇到了一个操作4,那么就把数组里的数一个一个插入到数据结构中
对于操作2,求区间和,也很中规中矩,很多有序的数据结构都可以支持区间和查询,对于另一部分的数组,也可以支持区间和查询
然后再看操作3,整体异或
我们先来考虑后面的数组如何整体异或:首先为了查询区间和,数组肯定要求前缀和,那么我们如何根据整体异或的修改来改变前缀和呢?其实很简单,我们不要单纯的前缀和,我们记下每一个二进制位(0,1)的前缀和,那么根据当前的整体异或值xortag的各个位的0/1情况,我们就知道贡献是多少。
然后考虑“数据结构”,很自然根据异或就想到Trie树,我们来看看Trie树如何满足所有的操作
操作1:不关Trie树的事
操作2:求区间和->求前缀和,注意到一个性质,那就是本题Trie树所管辖的下标区间,数字都是有序的,所以这里相当于求Trie树中最小的x个数的和;只需要记录每个点下面数字的个数size就行了;
操作3:对于一个整体标记tag,我们可以直接代数运算,这里主要提一下对于当前一个存在的tag,我们如何进行操作2的询问。我们将tag的每一位分解在Trie树上走,如果某一位tag为1,那么其实相当于左儿子'0'比右儿子'1'大,只需要颠倒判断一下即可
操作4:将数组中的元素一个一个插入到Trie树种即可
时间复杂度O((n+m)logn*logA)
细节:
1、注意,对于一个操作3,我们实际上并不能直接修改Trie树的tag值,因为修改了tag值其实表示我们的Trie树在当前tag值下有序,而一次操作3,但没有经过操作4,我们的Trie树对应的位置是不一定有序的,那么怎么处理呢?可以分开保存两个标记,一个tag表示当前Trie树在异或tag意义下有序(即上个操作4后的结果),一个xortag表示当前真正的异或值,在Trie树中,我们查询的时候就根据tag来,get子树和的时候就根据xortag来;然后外面的数组就一直是xortag,遇到操作4就把xortag传给Trie树的tag
2、在Trie树中查找前x小的数字的和的时候,要注意这种情况:比如最小数字0出现了3次,我现在想求最小的前2个数字和,那么这个时候就需要特判
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5,maxh=30; 4 int xortag,n,m; 5 struct trie 6 { 7 int ch[maxn*maxh][2]; 8 int sz[maxn*maxh]; 9 int sum[maxn*maxh][maxh]; 10 int root=0; 11 int len=0; 12 int tag=0; 13 void insert(int x) 14 { 15 int u=root; 16 for(int i=maxh-1;i>=0;--i) 17 { 18 int id=((x&(1<<i))>0); 19 if(!ch[u][id]) ch[u][id]=++len; 20 u=ch[u][id]; 21 ++sz[u]; 22 for(int j=0;j<maxh;++j) 23 if(x&(1<<j)) sum[u][j]++; 24 } 25 } 26 long long getsum(int x) 27 { 28 long long ans=0; 29 for(int i=0;i<maxh;++i) 30 if(xortag&(1<<i)) ans+=(sz[x]-sum[x][i])*(1LL<<i);else ans+=sum[x][i]*(1LL<<i); 31 return ans; 32 } 33 long long query(int x) 34 { 35 if(x==0) return 0; 36 int u=root; 37 long long ans=0; 38 for(int i=maxh-1;i>=0;--i) 39 { 40 int l=0,r=1; 41 if(tag&(1<<i)) swap(l,r); 42 if(x<=sz[ch[u][l]]) u=ch[u][l]; 43 else 44 { 45 ans+=getsum(ch[u][l]); 46 x-=sz[ch[u][l]]; 47 u=ch[u][r]; 48 } 49 } 50 ans+=getsum(u)/sz[u]*x; 51 return ans; 52 } 53 int getsize() 54 { 55 return sz[ch[root][0]]+sz[ch[root][1]]; 56 } 57 }Trie; 58 struct array 59 { 60 int a[maxn+50]; 61 int sum[maxn+50][maxh]; 62 int len=0; 63 void insert(int x) 64 { 65 x^=xortag; 66 a[++len]=x; 67 for(int i=0;i<maxh;++i) 68 sum[len][i]=sum[len-1][i]+((x&(1<<i))>0); 69 } 70 long long query(int x) 71 { 72 long long ans=0; 73 for(int i=0;i<maxh;++i) 74 if(xortag&(1<<i)) ans+=(x-sum[x][i])*(1LL<<i);else ans+=sum[x][i]*(1LL<<i); 75 return ans; 76 } 77 void transfer() 78 { 79 Trie.tag=xortag; 80 for(int i=1;i<=len;++i) 81 Trie.insert(a[i]); 82 len=0; 83 } 84 }Array; 85 long long query(int x) 86 { 87 if(x<=Trie.getsize()) return Trie.query(x); 88 else return Trie.query(Trie.getsize())+ 89 Array.query(x-Trie.getsize()); 90 } 91 int main() 92 { 93 scanf("%d",&n); 94 for(int i=1;i<=n;++i) 95 { 96 int x; 97 scanf("%d",&x); 98 Array.insert(x); 99 } 100 scanf("%d",&m); 101 for(int i=1;i<=m;++i) 102 { 103 int op,x,y; 104 scanf("%d",&op); 105 if(op==1) 106 { 107 scanf("%d",&x); 108 Array.insert(x); 109 } 110 if(op==2) 111 { 112 scanf("%d%d",&x,&y); 113 printf("%lld ",query(y)-query(x-1)); 114 } 115 if(op==3) 116 { 117 scanf("%d",&x); 118 xortag^=x; 119 } 120 if(op==4) Array.transfer(); 121 } 122 return 0; 123 }