Little Sub and Sequence 可持久化字典树
题解:如果只有异或操作,就直接用可持久化字典树,根据异或的值找第k大,这题还有&和|操作。我们可以把&和|操作转移到异或操作上
考虑&x操作,对数有影响的是x为0的位置,他会把所有的数的这一位全置0,我们可以把所有的数这一位全置0,然后暴力重构可持久化字典树,然后把异或的值这位置0;
如果这一位已经重构过就只需要改变异或的值的这一位。这样最多重构32次
考虑|x操作,对数有影响的是x为1的位置,他会把所有的数的这一位全置1,我们可以把所有的数这一位全置0,然后暴力重构可持久化字典树,然后把异或的值这位置0;
#include<bits/stdc++.h> #include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<vector> #include<stack> #include<queue> #include<cmath> #include<map> #include<set> #define ll long long #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 #define ull unsigned long long #define pb push_back #define P pair<ll,ll> #define f first #define s second using namespace std; const int N=5e4+5; int tr[N*35][2],a[N],rt[N],cn[N*35],tot = 0,n,q; char op[10]; bool vis[35]; int x; void update(int pre,int &rt,int w) { rt=++tot; int now=rt; for(int i=30;i>=0;i--) { if((w>>i)&1)tr[now][0]=tr[pre][0],tr[now][1]=++tot; else tr[now][1]=tr[pre][1],tr[now][0]=++tot; now=tr[now][(w>>i)&1]; pre=tr[pre][(w>>i)&1]; cn[now]=cn[pre]+1; } } void rebuild(int p,int op) { if(!vis[p]) { for(int i=1;i<=n;i++) { if((a[i]>>p)&1)a[i]-=1<<p; } vis[p]=1; tot=0; for(int i=1;i<=n;i++) { update(rt[i-1],rt[i],a[i]); } } if(op==0) { if((x>>p)&1)x-=1<<p; } else x|=1<<p; } int query(int l,int r,int k) { int ans=0; for(int i=30;i>=0;i--) { int id =(x>>i)&1; if(cn[tr[r][id]]-cn[tr[l][id]]>=k) { l=tr[l][id]; r=tr[r][id]; } else { k-=cn[tr[r][id]]-cn[tr[l][id]]; l=tr[l][id^1];r=tr[r][id^1]; ans+=1<<i; } } return ans; } int main() { scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } for(int i=1;i<=n;i++) { update(rt[i-1],rt[i],a[i]); } while(q--) { int w,l,r; scanf("%s", op); if(op[0]=='X') { scanf("%d",&w); x^=w; } else if(op[1]=='n') { scanf("%d",&w); for(int i=30;i>=0;i--) { if((w>>i)&1) continue; rebuild(i,0); } } else if(op[0]=='O') { scanf("%d",&w); for(int i=30;i>=0;i--) { if((w>>i)&1) { rebuild(i,1); } } } else { scanf("%d %d %d",&l,&r,&w); printf("%d\n",query(rt[l-1],rt[r],w)); } } }