测试地址:异或运算
题目大意:给定两个数列
做法:因为要去参加THUSC2017了,所以就想来做一下历年的题,结果……看到题目就蒙圈了,什么鬼?看了题解才知道是还没有学习的东西,于是紧赶慢赶一个小时把这个新东西学了。
这题的正确做法是可持久化trie树,也就是可持久化字典树。
初看这题,如果暴力求出所有异或出来的值,然后整体二分的话,总复杂度应为
注意到
然而还要考虑子矩阵的限制。发现
再来理一下整个解题的过程:用
犯二的地方:空间复杂度算错导致开小了数组,RE了两次,下次要注意…
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,q,rt[300010]={0},sum[9600010]={0},ch[9600010][2]={0},tot=0;
int nowl[1010],nowr[1010],bt[32];
bool x[1010][32]={0};
void insert(int last,int &no,int v,int d)
{
no=++tot;
sum[no]=sum[last],ch[no][0]=ch[last][0],ch[no][1]=ch[last][1];
if (d<0) {sum[no]++;return;}
if (v&bt[d]) insert(ch[last][1],ch[no][1],v,d-1);
else insert(ch[last][0],ch[no][0],v,d-1);
sum[no]=sum[ch[no][0]]+sum[ch[no][1]];
}
int query(int u,int d,int l,int r,int k)
{
for(int i=u;i<=d;i++)
nowl[i]=rt[l-1],nowr[i]=rt[r];
int ans=0,s;
for(int j=30;j>=0;j--)
{
s=0;
for(int i=u;i<=d;i++)
s+=sum[ch[nowr[i]][!x[i][j]]]-sum[ch[nowl[i]][!x[i][j]]];
if (s>=k)
{
for(int i=u;i<=d;i++)
{
nowl[i]=ch[nowl[i]][!x[i][j]];
nowr[i]=ch[nowr[i]][!x[i][j]];
}
ans+=bt[j];
}
else
{
for(int i=u;i<=d;i++)
{
nowl[i]=ch[nowl[i]][x[i][j]];
nowr[i]=ch[nowr[i]][x[i][j]];
}
k-=s;
}
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int a,j=0;
scanf("%d",&a);
while(a)
{
x[i][j]=a&1;
a>>=1;j++;
}
}
bt[0]=1;
for(int i=1;i<=30;i++) bt[i]=bt[i-1]<<1;
for(int i=1;i<=m;i++)
{
int a;
scanf("%d",&a);
insert(rt[i-1],rt[i],a,30);
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int u,d,l,r,k;
scanf("%d%d%d%d%d",&u,&d,&l,&r,&k);
printf("%d
",query(u,d,l,r,k));
}
return 0;
}