【CF840E】In a Trap
题意:一棵n个点的树,第i个点权值为ai,q次询问,每次给定u,v(u是v的祖先),求对于所有在u-v上的点i,$a_i\ \mathrm{xor}\ dis(i,v)$的最大值。
$n\le 50000,q\le 150000,a_i\le n$
题解:考虑分块,每块大小为$2^8=256$,对于一个点v,我们从v往上走,每经过256个点便分为一块。即,对于一个块中的所有i,dis(i,v)的前8位是相同的,并且后8位是0...255,那么我们可以令块内每个i的权值变为{0...255} xor ai,此时我们只需要考虑询问的前8位就行了。那么我们令f[a][b]表示第a个块遇到一个前8位是b的询问时,答案的最大值是多少。我们可以将{0...255} xor ai扔到Trie树里,就能预处理出f数组了。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn=50010; const int B=256; int f[maxn][B+4],fa[maxn][B+4],dep[maxn],head[maxn],nxt[maxn<<1],to[maxn<<1],ch[maxn][2],mx[maxn],v[maxn]; int n,m,tot,ans,cnt; inline void insert(int v) { int i,x=1,d; for(i=15;i>=8;i--) { d=(v>>i)&1; if(!ch[x][d]) ch[x][d]=++tot,ch[tot][0]=ch[tot][1]=0,mx[tot]=0; x=ch[x][d]; } mx[x]=max(mx[x],v&255); } inline int query(int v) { int i,x=1,ret=0,d; for(i=7;i>=0;i--) { d=!((v>>i)&1); if(!ch[x][d]) d^=1; else ret+=1<<(i+8); x=ch[x][d]; } ret+=mx[x]; return ret; } void dfs(int x) { int i; fa[x][0]=x; for(i=2;i<=B;i++) fa[x][i]=fa[fa[x][1]][i-1]; tot=1,ch[1][0]=ch[1][1]=0; for(i=0;i<B;i++) if(fa[x][i]) insert(v[fa[x][i]]^i); for(i=0;i<B;i++) f[x][i]=query(i); for(i=head[x];i!=-1;i=nxt[i]) if(to[i]!=fa[x][1]) dep[to[i]]=dep[x]+1,fa[to[i]][1]=x,dfs(to[i]); } inline void add(int a,int b) { to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++; } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i,j,t,a,b; for(i=1;i<=n;i++) v[i]=rd(); memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a); dep[1]=1,dfs(1); for(i=1;i<=m;i++) { b=rd(),a=rd(),t=0,ans=0; while(dep[fa[a][B-1]]>=dep[b]) ans=max(ans,f[a][t]),t++,a=fa[a][B]; for(j=0;j<=dep[a]-dep[b];j++) ans=max(ans,(t<<8|j)^v[fa[a][j]]); printf("%d\n",ans); } return 0; }//5 3 0 3 2 1 4 1 2 2 3 3 4 3 5 1 4 1 5 2 4