Description
给定长度为n的数列X={x1,x2,...,xn}和长度为m的数列Y={y1,y2,...,ym},令矩阵A中第i行第j列的值Aij=xi xor yj,每次询问给定矩形区域i∈[u,d],j∈[l,r],找出第k大的Aij。
Input
第一行包含两个正整数n,m,分别表示两个数列的长度
第二行包含n个非负整数xi
第三行包含m个非负整数yj
第四行包含一个正整数p,表示询问次数
随后p行,每行均包含5个正整数,用来描述一次询问,每行包含五个正整数u,d,l,r,k,含义如题意所述。
Output
共p行,每行包含一个非负整数,表示此次询问的答案。
Sample Input
3 3
1 2 4
7 6 5
3
1 2 1 2 2
1 2 1 3 4
2 3 2 3 4
Sample Output
6
5
1
HINT
对于100%的数据,0<=Xi,Yj<2^31,
1<=u<=d<=n<=1000,
1<=l<=r<=m<=300000,
1<=k<=(d-u+1)*(r-l+1),
1<=p<=500
Sol
这题最重要的是观察数据范围,我们发现p和n都很小,每组询问我们可以枚举n,然后使用数据结构去处理m那一维。我们可以把n个数放到一块,然后每次按位考虑,如果这一位能使答案为1的数字个数大于等于k了,就说明答案的这一位是1,然后我们继续往下走。如果不行,那么我们把k减去sum,然后往相反的方向走,这样的操作实际上就相当于在主席树上二分。
时间复杂度(O(nqlogm))。
Code
#include <cstdio>
int ch[10000005][2],tot,sz[10000005],rt[300005],n,m,v[300005],q,u,d,l,r,K,x,a[300005],b[300005],c[300005];
int ins(int x,int v)
{
int tp=++tot,u=tp,f;
for(int i=(1<<30);i;i>>=1) f=(v&i)>0,ch[u][f]=++tot,ch[u][f^1]=ch[x][f^1],u=ch[u][f],x=ch[x][f],sz[u]=sz[x]+1;
return tp;
}
int que(int len,int k)
{
int res=0,d,f,sum;
for(int i=(1<<30),j;i;i>>=1)
{
for(j=1,sum=0;j<=len;j++) d=!(a[j]&i),sum+=sz[ch[c[j]][d]]-sz[ch[b[j]][d]];
if(sum>=k) f=0,res|=i;else f=1,k-=sum;
for(j=1;j<=len;j++) d=(!(a[j]&i))^f,b[j]=ch[b[j]][d],c[j]=ch[c[j]][d];
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
for(int i=1;i<=m;i++) scanf("%d",&x),rt[i]=ins(rt[i-1],x);
for(scanf("%d",&q);q--;printf("%d
",que(d-u+1,K)))
{
scanf("%d%d%d%d%d",&u,&d,&l,&r,&K);
for(int j=u;j<=d;j++) a[j-u+1]=v[j],b[j-u+1]=rt[l-1],c[j-u+1]=rt[r];
}
}