一个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)); } }