[cf1515H]Phoenix and Bits
记$V=2^{20}-1$,即值域范围,也可以作为"全集"
显然与$a_{i}$的顺序无关,对所有$a_{i}$维护一棵trie树
关于如何维护这棵trie树,考虑使用分裂+合并的方式,即:1.分裂出区间对应的trie树;2.操作分裂出的trie树;3.合并分裂出的tire树和原trie树
(关于时间复杂度后面会进行统一分析,暂时不需要考虑)
对于分裂(第1步),使用类似线段树查询的方法
对于合并(第3步),使用类似线段树合并的方法(即在其中一个子树为空时直接选择另一个子树)
对于操作(第2步),对不同的操作类型分类讨论——
查询操作:维护子树大小(子树内数值数量),直接输出即可
与操作:将其转化为异或$V$、或$x\oplus V$、异或$V$,那么只需要考虑异或和或操作即可
异或操作:直接在根节点上打懒标记,对于区间$[l,r]$的懒标记$tag$仅考虑$tag\and (r-l)$的结果,因此在下传标记时,若$tag\and \frac{r-l+1}{2}$非0则交换$[l,r]$的左右儿子
或操作:递归所有节点,当递归到区间$[l,r]$时,若$x\and \frac{r-l+1}{2}$非0则在$[l,r]$的左子树上打异或$\frac{r-l+1}{2}$的懒标记并合并$[l,r]$的左右子树(其中$x$为操作权值),再做如下剪枝——
维护子树与(子树内所有数值的与)和子树或,设当前子树两者分别为$v_{1}$和$v_{2}$,若$x\and (v_{1}\oplus v_{2}\oplus V)=x$(即子树内所有权值在$x$为1的位上都相同)则直接打上异或$x\and (v_{1}\oplus V)$的懒标记即可
("在$[l,r]$的左子树上打异或$\frac{r-l+1}{2}$的懒标记"的实际目的是维护子树与和子树或)
下面,来分析时间复杂度:
定义节点$x$的势能为$1+\log V+(v_{1}\oplus v_{2})$中1的个数(其中$v_{1}$和$v_{2}$为子树与和子树或,后者即$x$子树中仍未完全相同的位数),分别考虑这些操作的均摊复杂度——
分裂新建$o(\log V)$个节点,一个节点的势能为$o(\log V)$,即$o(\log^{2}V)$
合并递归过程中,若继续递归,即会合并两个节点,合并后势能减少$o(1)$,即均摊复杂度为$o(1)$
异或(打懒标记)不影响势能,即均摊复杂度为$o(1)$
或操作递归过程中,若不满足剪枝,必然会使得$v_{1}\oplus v_{2}$中1的个数减少1个,即均摊复杂度为$o(1)$
综上所述,总均摊复杂度为$o(n\log^{2}V)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 8000005 4 #define V ((1<<20)-1) 5 #define mid (l+r>>1) 6 int V_trie,rt,n,m,p,x,y,z,ls[N],rs[N],sz[N],And[N],Or[N],tag[N]; 7 int New(){ 8 int k=++V_trie; 9 And[k]=V; 10 return k; 11 } 12 void upd_xor(int k,int l,int r,int x){ 13 if (!k)return; 14 tag[k]^=x; 15 int p=((And[k]&(x^V))|((Or[k]^V)&x)); 16 Or[k]=((Or[k]&(x^V))|((And[k]^V)&x)); 17 And[k]=p; 18 } 19 void up(int k){ 20 sz[k]=sz[ls[k]]+sz[rs[k]]; 21 And[k]=(And[ls[k]]&And[rs[k]]); 22 Or[k]=(Or[ls[k]]|Or[rs[k]]); 23 } 24 void down(int k,int l,int r){ 25 if (tag[k]&((r-l+1)>>1))swap(ls[k],rs[k]); 26 upd_xor(ls[k],l,mid,tag[k]); 27 upd_xor(rs[k],mid+1,r,tag[k]); 28 tag[k]=0; 29 } 30 void add(int &k,int l,int r,int x){ 31 if (!k)k=New(); 32 if (l==r){ 33 sz[k]=1,And[k]=Or[k]=x; 34 return; 35 } 36 if (x<=mid)add(ls[k],l,mid,x); 37 else add(rs[k],mid+1,r,x); 38 up(k); 39 } 40 int split(int &k,int l,int r,int x,int y){ 41 if ((!k)||(l>y)||(x>r))return 0; 42 if ((x<=l)&&(r<=y)){ 43 int p=k; 44 k=0; 45 return p; 46 } 47 down(k,l,r); 48 int kk=New(); 49 ls[kk]=split(ls[k],l,mid,x,y); 50 rs[kk]=split(rs[k],mid+1,r,x,y); 51 up(k),up(kk); 52 return kk; 53 } 54 void merge(int &k1,int k2,int l,int r){ 55 if ((!k1)||(!k2)){ 56 k1+=k2; 57 return; 58 } 59 if (l==r){ 60 sz[k1]=max(sz[k1],sz[k2]); 61 And[k1]&=And[k2]; 62 Or[k1]|=Or[k2]; 63 return; 64 } 65 down(k1,l,r),down(k2,l,r); 66 merge(ls[k1],ls[k2],l,mid); 67 merge(rs[k1],rs[k2],mid+1,r); 68 up(k1); 69 } 70 void upd_or(int k,int l,int r,int x){ 71 if (!k)return; 72 if ((x&(And[k]^Or[k]^V))==x){ 73 upd_xor(k,l,r,(x&(And[k]^V))); 74 return; 75 } 76 down(k,l,r); 77 if (x&((r-l+1)>>1)){ 78 upd_xor(ls[k],l,mid,((r-l+1)>>1)); 79 merge(rs[k],ls[k],mid+1,r); 80 ls[k]=0; 81 } 82 upd_or(ls[k],l,mid,x); 83 upd_or(rs[k],mid+1,r,x); 84 up(k); 85 } 86 int main(){ 87 scanf("%d%d",&n,&m); 88 And[0]=V; 89 for(int i=1;i<=n;i++){ 90 scanf("%d",&x); 91 add(rt,0,V,x); 92 } 93 for(int i=1;i<=m;i++){ 94 scanf("%d%d%d",&p,&x,&y); 95 int k=split(rt,0,V,x,y); 96 if (p==4)printf("%d\n",sz[k]); 97 else{ 98 scanf("%d",&z); 99 if (p==1){ 100 upd_xor(k,0,V,V); 101 upd_or(k,0,V,(z^V)); 102 upd_xor(k,0,V,V); 103 } 104 if (p==2)upd_or(k,0,V,z); 105 if (p==3)upd_xor(k,0,V,z); 106 } 107 merge(rt,k,0,V); 108 } 109 }