@xiaoziyao
2020-10-25T11:49:54.000000Z
字数 1871
阅读 1517
解题报告
这道题的测试点数是真的多。。。
一道猫树分治+线性基题。
猫树分治的操作:从区间中选一个分治中点,预处理的信息,预处理的信息,然后对所有跨越了的询问快速合并,不经过直接向子区间分治。
这道题我们可以直接把询问离线,然后上猫树分治。
具体地,我们用的暴力预处理和的线性基(其中),然后在合并的时候用的暴力合并跨越的两个线性基。
总复杂度:。
#include<stdio.h>const int maxn=500005,maxm=500005,maxk=31;int n,m;int a[maxn],ans[maxm];struct question{//询问int l,r,id;}q[maxm],tmp1[maxm],tmp2[maxm];struct base{//线性基int p[maxk];void clear(){for(int i=0;i<maxk;i++)p[i]=0;}}b[maxn];void insert(base &now,int x){//线性基插入for(int i=30;i>=0;i--){if(((x>>i)&1)==0)continue;if(now.p[i]==0){now.p[i]=x;return ;}x^=now.p[i];}}base merge(base a,base b){//线性基合并for(int i=0;i<=30;i++)if(b.p[i])insert(a,b.p[i]);return a;}int getmax(base x){//线性基求异或最大值int res=0;for(int i=30;i>=0;i--)if((res^x.p[i])>res)res^=x.p[i];return res;}void solve(int l,int r,int ql,int qr){//猫树分治if(ql>qr)return ;if(l==r){for(int i=ql;i<=qr;i++)ans[q[i].id]=a[l];return ;}int mid=(l+r)>>1,cnt1=0,cnt2=0;b[mid].clear(),insert(b[mid],a[mid]);for(int i=mid-1;i>=l;i--)b[i]=b[i+1],insert(b[i],a[i]);for(int i=mid+1;i<=r;i++)b[i]=b[i-1],insert(b[i],a[i]);for(int i=ql;i<=qr;i++){//合并左右预处理过的线性基if(l<=q[i].l&&q[i].l<=mid&&mid+1<=q[i].r&&q[i].r<=r){ans[q[i].id]=getmax(merge(b[q[i].l],b[q[i].r]));continue;}if(q[i].l<=mid&&q[i].r<=mid)tmp1[++cnt1]=q[i];else tmp2[++cnt2]=q[i];}for(int i=1;i<=cnt1;i++)q[ql+i-1]=tmp1[i];for(int i=1;i<=cnt2;i++)q[ql+cnt1+i-1]=tmp2[i];solve(l,mid,ql,ql+cnt1-1),solve(mid+1,r,ql+cnt1,ql+cnt1+cnt2-1);//分治解决子问题}int main(){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);scanf("%d",&m);for(int i=1;i<=m;i++){scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;}solve(1,n,1,m);for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return 0;}
