Description
“~妖梦,我又饿了!”
魂魄妖梦身为西行寺家的专属庭师第二代兼大小姐的西行寺幽幽子的护卫,却承担了为幽幽子准备食物的任务。幽幽子是个非常贪吃的亡灵,所以妖梦经常为食物的问题所困。
现在,妖梦有n盘食物排成一排,第i盘食物有一个属性a[i]。亡灵的体质比较特殊,所以妖梦认为食物的属性很重要。妖梦会进行q次询问,每次给出两个整数r,k,她想知道有多少个区间[i,r](1≤i≤r),区间内所有食物属性值的异或大于等于k。
Input
第一行一个整数n,接下来n个非负整数,表示每盘食物的属性。
接下来一行,一个整数q,表示询问个数。接下来q行,每行两个整数r,k,意义如题目所述。
Output
q行,每行一个整数表示该询问的答案。
Sample Input
4
1 2 3 4
3
2 2
4 5
3 2
Sample Output
2
2
1
Data Constraint
30%:n,q≤1000
另有30%数据:n≤1000且询问的r是不下降的
100%:1≤n,q≤100000 r≤n 0≤k,a[i]≤1,000,000,000
Solution
对于30%,直接枚举n,判断是否合法,复杂度
O(nq)
然而,由于OJ跑的比较快,另外30分也顺便过了。
对于100%
发现n和q比较大,a[i]也很大,考虑使用trie树离线操作
设一个x[i]表示a[1]^a[2]^a[3]^……^a[i],从1向右枚举,每次把x[i]加入trie,遇到查询就把x[r]与trie之前的查询判断大于等于k的有多少个,然后,就没有然后了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 101000
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m,a[N],t[10000000][3],tot=0,c[31],d[31],ans[N];
struct note{
int r,k,s;
};
note q[N];
bool cnt(note x,note y){return x.r<y.r;}
void put(int x)
{
memset(c,0,sizeof(c));int j=31;
for(int k=x;k;k/=2) c[--j]=k%2;
int ls=0;
fo(i,1,30)
{
int y=c[i];
if (t[ls][y]==0){
t[ls][y]=++tot;ls=tot;t[ls][2]++;
}
else{
ls=t[ls][y];t[ls][2]++;
}
}
}
int get(int x,int v,int y)
{
if (v==0) return 0;
if (c[x]^y==0)
{
if (d[x]==1) return t[v][2];
}
else
{
if(d[x]==0) return 0;
}
return get(x+1,t[v][0],0)+get(x+1,t[v][1],1);
}
int main()
{
freopen("food.in","r",stdin);
freopen("food.out","w",stdout);
scanf("%d",&n);fo(i,1,n) scanf("%d",&a[i]);
scanf("%d",&m);
fo(i,1,m) scanf("%d%d",&q[i].r,&q[i].k),q[i].s=i;
sort(q+1,q+m+1,cnt);
int j=1,jy=0;
fo(i,1,n)
{
jy^=a[i];
while (i==q[j].r) {
memset(d,0,sizeof(d));int l=31;
for(int k=q[j].k;k;k/=2) d[--l]=k%2;
memset(c,0,sizeof(c));l=31;
for(int k=jy;k;k/=2) c[--l]=k%2;
ans[q[j].s]=q[j].r-get(1,t[0][0],0)-get(1,t[0][1],1);
if (jy<q[j].k) ans[q[j].s]--;
j++;
}
put(jy);
}
fo(i,1,m) printf("%d\n",ans[i]);
}