bzoj4571: [Scoi2016]美味
这道题想了好几天了。。。可持久化trie完全做不动
然后不知道谁给了个路牌,今天突然会了
拿个主席树来,维护一下权值线段树,一开始我们想让最高位为1,那么就看看对于b取0还是1,假如没有加的操作,区间就是0~2^j-1上的数该位是0,2^j~2^(j+1)-1是1,加上的数就相当于这些区间都减掉这个数,据此用类似可持久化trie的思想,在左右区间找并不断更新区间lr即可
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; struct chairman { int lc,rc,c; }tr[6100000];int trlen,rt[210000]; int maketree(int x,int l,int r,int p) { if(x==0) { x=++trlen; tr[x].lc=tr[x].rc=0; } tr[x].c++; if(l<r) { int mid=(l+r)/2; if(p<=mid)tr[x].lc=maketree(tr[x].lc,l,mid,p); else tr[x].rc=maketree(tr[x].rc,mid+1,r,p); } return x; } int merge(int x,int y) { if(x==0||y==0)return x+y; tr[x].c+=tr[y].c; tr[x].lc=merge(tr[x].lc,tr[y].lc); tr[x].rc=merge(tr[x].rc,tr[y].rc); return x; } bool check(int x,int y,int ml,int mr,int l,int r) { if(l<0)l=0; if(x==0&&y==0)return false; if(ml==l&&mr==r)return (tr[y].c-tr[x].c)>0; int mid=(ml+mr)/2; if(r<=mid) return check(tr[x].lc,tr[y].lc,ml,mid,l,r); else if(mid+1<=l)return check(tr[x].rc,tr[y].rc,mid+1,mr,l,r); else return check(tr[x].lc,tr[y].lc,ml,mid,l,mid)|check(tr[x].rc,tr[y].rc,mid+1,mr,mid+1,r); } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int n,Q,x; scanf("%d%d",&n,&Q); trlen=0;memset(rt,0,sizeof(rt)); for(int i=1;i<=n;i++) { scanf("%d",&x); rt[i]=maketree(rt[i],0,(1<<23)-1,x); rt[i]=merge(rt[i],rt[i-1]); } while(Q--) { int g,d,st,ed; scanf("%d%d%d%d",&g,&d,&st,&ed); int L=-d,R=(1<<23)-1-d,ans=0; for(int i=22;i>=0;i--) { int mid=(L+R)>>1; int x=(g&(1<<i)); if(x==0) { if(check(rt[st-1],rt[ed],0,(1<<23)-1,mid+1,R)) ans+=(1<<i), L=mid+1; else R=mid; } else { if(check(rt[st-1],rt[ed],0,(1<<23)-1,L,mid)) ans+=(1<<i), R=mid; else L=mid+1; } } printf("%d\n",ans); } return 0; }
pain and happy in the cruel world.