【BZOJ4103】[Thu Summer Camp 2015]异或运算 可持久化Trie树

【BZOJ4103】[Thu Summer Camp 2015]异或运算

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

题解:由于n和p很小,考虑暴力枚举行。我们将n个x值都拿出来,对于y维护可持久化Trie树,然后将这些x一起放到可持久化Trie树上二分即可。集体做法与主席树的求第k大类似。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,m,tot,Q;
struct node
{
	int ch[2],siz;
}s[10000010];
int v[1010],p[1010],p1[1010],p2[1010],rt[300010];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int insert(int x,int val)
{
	int u,tmp=++tot,d,i;
	for(u=tmp,i=1<<30;i;i>>=1)
	{
		d=(val&i)>0,s[u].ch[d]=++tot,s[u].ch[d^1]=s[x].ch[d^1];
		u=s[u].ch[d],x=s[x].ch[d],s[u].siz=s[x].siz+1;
	}
	return tmp;
}
int query(int len,int k)
{
	int i,j,ret=0,d,dd,sum;
	for(i=1<<30;i;i>>=1)
	{
		for(sum=0,j=1;j<=len;j++)
		{
			d=!(p[j]&i);
			sum+=s[s[p2[j]].ch[d]].siz-s[s[p1[j]].ch[d]].siz;
		}
		if(sum>=k)	dd=0,ret|=i;
		else	dd=1,k-=sum;
		for(j=1;j<=len;j++)
		{
			d=(!(p[j]&i))^dd;
			p2[j]=s[p2[j]].ch[d],p1[j]=s[p1[j]].ch[d];
		}
	}
	return ret;
}
int main()
{
	n=rd(),m=rd();
	int i,j,a,b,c,d;
	for(i=1;i<=n;i++)	v[i]=rd();
	for(i=1;i<=m;i++)	rt[i]=insert(rt[i-1],rd());
	Q=rd();
	for(i=1;i<=Q;i++)
	{
		a=rd(),b=rd(),c=rd(),d=rd();
		for(j=a;j<=b;j++)	p[j-a+1]=v[j],p1[j-a+1]=rt[c-1],p2[j-a+1]=rt[d];
		printf("%d\n",query(b-a+1,rd()));
	}
	return 0;
}//3 3 1 2 4 7 6 5 3 1 2 1 2 2 1 2 1 3 4 2 3 2 3 4
posted @ 2017-09-10 09:39  CQzhangyu  阅读(283)  评论(0编辑  收藏  举报