一个th的题(a gensokyo)
难度系数在该知识点下为$2.1$
区间xor我们很明显会想到trie树,将每一个区间$l~r$异或和拆成$sum[l-1]$ $sum[r]$两个数的异或
注意到二进制的性质,比当前低的位即使都取1加起来都没有这位选1答案高,所以考虑贪心
题目中每次查询的范围都被限定在一个区间,所以考虑弄出了“$1~r$范围中所有前缀异或和的trie”后怎么搞
考虑每一位,当这一位的某一个儿子在$s[r]$与当前trie节点xor为1,就设这个儿子为”大儿子”,否则是“小儿子”,分别记为$a$,$b$
记$ed[x]$表示在插入以后,$x$这个节点代表的数出现个数,$siz[x]$表示$x$节点子树的所有节点$ed$值的和
根据异或的性质,如果$k$在当前考虑的这一位值为0,那么就将答案加上$sz[b]$(因为b所有子树代表值都比$k$大),然后考虑下一位
否则,不累加答案,考虑下一位
当位数超出限制,返回当前节点$ed$值,当现在树为空,返回$0$
于是本题成功解决
#include<bits/stdc++.h> using namespace std; #define N 100010 typedef unsigned int ui; ui n,k,a[N],ct,nc,sz[N<<6],tw[N],s[N]; int c[N<<6][2],d[N][35],rt[N],qq; void ins(int p,int &o,int x,int t){if(!o)o=++nc;sz[o]=sz[p]+1;if(x>32)return;c[o][!d[t][x]]=c[p][!d[t][x]];ins(c[p][d[t][x]],c[o][d[t][x]],x+1,t); } ui q(int o,int k,int x,int t){if(x>32||!o)return sz[o];int a=c[o][d[t][x]],b=c[o][!d[t][x]],p=k&tw[32-x];if(!p)return q(a,k,x+1,t)+sz[b];return q(b,k,x+1,t); } int main(){freopen("food.in","r",stdin);freopen("food.out","w",stdout);scanf("%u",&n);tw[0]=1;for(int i=1;i<=32;i++)tw[i]=tw[i-1]*2ll;for(int i=1;i<=n;i++){scanf("%u",&a[i]);s[i]=s[i-1]^a[i];}for(int i=1;i<=n;i++){ui x=s[i];ct=0;while(x){d[i][++ct]=x&1;x/=2;}reverse(d[i]+1,d[i]+33);ins(rt[i-1],rt[i],1,i-1);}scanf("%d",&qq);while(qq--){int r,k;scanf("%d%d",&r,&k);printf("%u\n",q(rt[r],k,1,r));} }