【问题描述】
给出n个非负整数A[1]..A[n],编程回答询问:
1 k x:询问 x xor A[i] (1<=i<=n) 中第k小的值。
2 k l r x:询问 x xor A[i] (l<=i<=r) 中第k小的值。
【输入格式】
第一行为整数n。
第二行为n个非负整数,表示A[1],A[2],…,A[n]。
第三行为整数m。
之后的m行,每行表示一种询问。
【输出格式】
对于每个询问,给出你的回答。
【输入样例】
5
1 2 3 4 5
2
1 4 3
2 2 3 5 2
【输出样例】
6
6
【数据范围】
n,m<=500000
A[i],x均在unsigned long long 范围,所有询问均合法
【来源】
Mr_he原创
一道可持久化的二进制trie树,写法类似权值线段树的Kth,只需要记录每个点的下面包含多少个数就可以了。
详细代码如下:
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
typedef unsigned long long ull;
const int maxn=500005;
int root[maxn],s[maxn*32]={0},ch[maxn*32][2]={0},cnt=0,n;
void up(int x)
{
s[x]=s[ch[x][0]]+s[ch[x][1]];
}
void in(int pre,int &now,int i,ull x)
{
now=++cnt;
ch[now][0]=ch[pre][0];
ch[now][1]=ch[pre][1];
s[now]=s[pre];
if(i<0) {s[now]++;return;}
int d=(x>>i)&1;
in(ch[pre][d],ch[now][d],i-1,x);
up(now);
}
ull read()
{
ull x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
ull find(int pre,int now,int i,int k,ull &x)
{
if(i<0) return 0;
int d=(x>>i)&1;
int t=s[ch[now][d]]-s[ch[pre][d]];
if(t<k)
{
return find(ch[pre][d^1],ch[now][d^1],i-1,k-t,x)+((ull)1<<i);
}
else return find(ch[pre][d],ch[now][d],i-1,k,x);
}
char q[50];
void out(ull x)
{
if(!x)
putchar('0');
int tot=0;
while(x)
{
q[++tot]=x%10;
x/=10;
}
for(int i=tot;i>=1;i--) putchar(q[i]+'0');
putchar('\n');
return;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
n=read();
ull x,ans;
for(int i=1;i<=n;i++)
{
x=read();
in(root[i-1],root[i],63,x);
}
int l,r,k,id;
int m=read();
while(m--)
{
id=read();
if(id==1)
{
k=read();x=read();
ans=find(root[0],root[n],63,k,x);
}
if(id==2)
{
k=read();l=read();r=read();x=read();
ans=find(root[l-1],root[r],63,k,x);
}
out(ans);
}
return 0;
}