bzoj4137[FJOI2015]火星商店问题
分析:
首先,一看到求val xor x最大,我们就应该想到可持久化Trie,(详见P4585),这样,当我们询问 L,RL,RL,R 之间的最大异或值时,就直接套用可持久化Trie就行了,这样空间这一维就可以处理了。如果全是特殊商品的话,这个问题就解决了,但还有时间这一维啊,怎么办?(把出题人吊起来打一顿,让他改数据)
时间怎么办?
首先,每个火星人能够买的商品的进货时间应该是在一段时间区间内(对普通商品而言),那么只有在这个区间内的商品才能被该火星人买,即对该次询问做贡献,而如果我们把商品看成一个点,那么某次询问就是对一段区间上所有的点做一次询问,那么,我们会发现:
这个可以用线段树维护
如果我们在线处理的话,就会重复运算很多次,就可能会 $TLE$ ,其实我也没试过,于是,我们就要引出这题正解了:
线段树分治
所谓线段树分治,就是把某一段区间用线段树的方法分成很多段子区间,再把修改和询问一起放在线段树上,然后再统一一起处理一遍,这样做的好处在于,可以省掉许多重复计算,提高效率。
好了,那么对于这题而言,只要用线段树的方法处理区间询问和单点修改就行了。也就是对于当前区间 $[L,R]$先把完全包含它的所有询问用这一区间内的点(商品)更新,再把在左/右区间的点和与左/右区间有交集且没有用该区间更新过的用的询问一起往下递归处理,直到没有询问传递下来为止。
具体看代码吧
Code
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<string> #include<iostream> #include<queue> #include<iomanip> #include<algorithm> using namespace std; const int N=100010; int n,m,day,ans[N]; struct node { int time,val,pos; }a[N*2]; int totm; struct que { int l,r,tl,tr,x; }q[N*2]; int totq,id[N],cnt; int ch[N<<5][2],root[N],sum[N<<5],totp; node tmpl[N*2],tmpr[N*2]; bool cmp(const node &a,const node &b) { return a.pos<b.pos; } void add(int x,int x1,int val)//可持久化trie { int i,j; sum[x]=sum[x1]+1; for(i=16;i>=0;i--) { bool f=(val&(1<<i)); ch[x][!f]=ch[x1][!f]; x1=ch[x1][f]; x=ch[x][f]=++cnt; sum[x]=sum[x1]+1; } } int quer(int x,int x1,int val)//可持久化trie { int ans=0,i; for(i=16;i>=0;i--) { ans<<=1; bool f=(val&(1<<i)); if(ch[x1][!f]>ch[x][!f]) ans++; else f=!f; x=ch[x][!f];x1=ch[x1][!f]; } return ans; } int find(int l,int r,int x) { int ml=l; if(a[l].pos>x) return 0; while(l<r) { int mid=l+r>>1; if(a[mid+1].pos<=x) l=mid+1; else r=mid; } return l-ml+1; } void work(int ml,int mr,int qr)//用可持久化trie更新询问 { totp=0;cnt=0; int i,j,k; for(i=ml;i<=mr;i++) { totp++;cnt++; root[totp]=cnt; add(root[totp],root[totp-1],a[i].val); } for(i=1;i<=qr;i++) { int l=find(ml,mr,q[id[i]].l-1); int r=find(ml,mr,q[id[i]].r); ans[id[i]]=max(ans[id[i]],quer(root[l],root[r],q[id[i]].x)); } } void slove(int ml,int mr,int tl,int tr,int tp) { if(ml>mr||tp==0) return; int tot=0,i,j; for(i=1;i<=tp;i++) if(q[id[i]].tl<=tl&&tr<=q[id[i]].tr) swap(id[i],id[++tot]); work(ml,mr,tot); if(tl==tr) return; int mid=tl+tr>>1,lt=0,rt=0; for(i=ml;i<=mr;i++) { if(a[i].time<=mid) tmpl[++lt]=a[i]; else tmpr[++rt]=a[i]; } for(i=1;i<=lt;i++) a[ml+i-1]=tmpl[i]; for(i=1;i<=rt;i++) a[ml+lt+i-1]=tmpr[i]; tot=0; for(i=1;i<=tp;i++) { if(q[id[i]].tl<=tl&&tr<=q[id[i]].tr) continue; if(q[id[i]].tl<=mid) swap(id[i],id[++tot]); } slove(ml,ml+lt-1,tl,mid,tot); tot=0; for(i=1;i<=tp;i++) { if(q[id[i]].tl<=tl&&tr<=q[id[i]].tr) continue; if(q[id[i]].tr>mid) swap(id[i],id[++tot]); } slove(ml+lt,mr,mid+1,tr,tot); } int main() { int i,j,op,k,d; totq=day=0; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i].val); a[i].pos=i;a[i].time=0; } int spt; spt=totm=n; for(i=1;i<=m;i++) { scanf("%d",&op); if(op==0) { day++;a[++totm].time=day; scanf("%d%d",&a[totm].pos,&a[totm].val); } else { totq++; id[totq]=totq; scanf("%d%d%d%d",&q[totq].l,&q[totq].r,&q[totq].x,&d); q[totq].tr=day;q[totq].tl=max(day-d+1,1); } } sort(a+spt+1,a+totm+1,cmp); work(1,spt,totq);//特殊商品单独处理 slove(spt+1,totm,1,day,totq);//线段树分治 for(i=1;i<=totq;i++) printf("%d\n",ans[i]); return 0; }