异或运算
1702:异或运算
时间限制: 2000 ms 内存限制: 524288 KB
【题目描述】
给定长度为n的数列X={x1,x2,...,xn}和长度为m的数列Y={y1,y2,...,ym},令矩阵A中第i行第j列的值Aij=xi^yj,每次询问给定矩形区域i∈[u,d],j∈[l,r],找出第k大的Aij。
【输入】
第一行包含两个正整数n,m,分别表示两个数列的长度;
第二行包含n个非负整数xi;
第三行包含m个非负整数yi;
第四行包含一个正整数p,表示询问次数;
随后p行,每行均包含5个正整数,用来描述一次询问,每行包含五个正整数u,d,l,r,k,含义如题意所述。
【输出】
共p行,每行包含一个非负整数,表示此次询问的答案。
【输入样例】
3 3
1 2 4
7 6 5
3
1 2 1 2 2
1 2 1 3 4
2 3 2 3 4
【输出样例】
6
5
1
【提示】
【数据规模】
对于100%的数据:
0≤Xi,Yj<pow(2,31),1≤u≤d≤n≤1000,1≤l≤r≤m≤300000,1≤k≤(d-u+1)×(r-l+1),1≤p≤500。
【题解】
首先考虑到枚举数的每一位,如果可以填1就这一位填1否则填0。
可以构造可持久化trie树,按位拆分加入每一个Y数组中元素。
查询:
因为p和n均很小,可以考虑枚举每一位A中元素。
查询时算出这一位取1时可以构造出好多数,如果>k则取1,否则取0,k-=个数。再将A每一位所对应的trie节点更新为其左或右儿子,即可 查出答案。
复杂度O(31*(n*p+m)).
代码如下:
#include<bits/stdc++.h> using namespace std; const int M=3e5+5; int n,m,root[M],a[1005],cnt,p,pr[1005],no[1005],ans; struct trie { int er[2],siz; }sh[M*33]; inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } inline void pushup(int now) { sh[now].siz=sh[sh[now].er[0]].siz+sh[sh[now].er[1]].siz; } inline int insert(int now,int pre,int x,int ge) { now=++cnt;sh[now]=sh[pre]; if(ge==-1) { sh[now].siz++; return now; } int hu=(x>>ge)&1; sh[now].er[hu]=insert(sh[now].er[hu],sh[pre].er[hu],x,ge-1); pushup(now); return now; } inline void query(int ge,int l,int r,int k) { int gu=0,pan=(1<<ge); if(ge==-1) return; for(int i=l;i<=r;i++) { if(a[i]&pan) gu+=sh[sh[no[i]].er[0]].siz-sh[sh[pr[i]].er[0]].siz; else gu+=sh[sh[no[i]].er[1]].siz-sh[sh[pr[i]].er[1]].siz; } if(gu>=k) { for(int i=l;i<=r;i++) { if(a[i]&pan) no[i]=sh[no[i]].er[0],pr[i]=sh[pr[i]].er[0]; else no[i]=sh[no[i]].er[1],pr[i]=sh[pr[i]].er[1]; } ans|=pan; query(ge-1,l,r,k); } else { for(int i=l;i<=r;i++) { if(a[i]&pan) no[i]=sh[no[i]].er[1],pr[i]=sh[pr[i]].er[1]; else no[i]=sh[no[i]].er[0],pr[i]=sh[pr[i]].er[0]; } query(ge-1,l,r,k-gu); } } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1,x;i<=m;i++) { x=read(); root[i]=insert(root[i],root[i-1],x,30); } p=read(); for(int i=1,x,y,z,w,u;i<=p;i++) { x=read();y=read();z=read();w=read();u=read(); for(int j=x;j<=y;j++) pr[j]=root[z-1],no[j]=root[w];//极角米巴游 ans=0; query(30,x,y,u); cout<<ans<<"\n"; } return 0; }
走上社会,很快会栽跟头
——胡向荣