[SCOI2016] 幸運數字
阿新 • • 發佈:2019-01-03
Description
給定一棵樹,每個點有點權。每次詢問兩個點 \(x,y\),求 \(x\) 到 \(y\) 的路徑上選擇若干個點的點權異或和最大值。\(n\leq 2\cdot 10^4,q\leq 2\cdot 10^5\)。
Solution
這種樹上路徑的題一般就是樹剖啊點分治啊倍增啊。
這題顯然要維護線性基,線性基合併最低複雜度 \(\log^2n\) 。
如果樹剖或者倍增維護線性基都是 \(q\log^3 n\) 的,但是都可以線上。
但是如果換成點分治做就是 \(q\log^2 n\) 十分優秀。
每次求出重心到每個點的線性基,只統計過重心的路徑的答案即可。
如果一條路徑只經過了一個點特判掉即可。
Code
#include<bits/stdc++.h> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) #define int long long const int B=62; const int N=20005; vector< pii > v[N]; int head[N],mx[N],vis[N],ok[N],val[N]; int n,m,cnt,tot,root,id,MX,sze[N],ans[N*10]; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } struct xxj{ int a[B]; xxj(){memset(a,0,sizeof a);} void clear(){memset(a,0,sizeof a);} void ins(int x){ for(int i=61;~i;i--){ if(x>>i&1){ if(!a[i]){ a[i]=x; return; } else x^=a[i]; } } } }f[N]; xxj hb(xxj a,xxj b){ xxj c=a; for(int i=61;~i;i--) if(b.a[i]) c.ins(b.a[i]); return c; } void getroot(int now,int fa=0){ mx[now]=0;sze[now]=1; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(vis[to] or to==fa) continue; getroot(to,now);sze[now]+=sze[to]; mx[now]=max(mx[now],sze[to]); } mx[now]=max(mx[now],tot-sze[now]); if(mx[now]<MX) MX=mx[now],root=now; } int getint(){ int X=0,w=0;char ch=getchar(); while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } int query(xxj a,int now=0){ for(int i=61;~i;i--) if((now^a.a[i])>now) now^=a.a[i]; return now; } void dfs(int now,int fa){ f[now]=f[fa];f[now].ins(val[now]); for(auto x:v[now]){ if(ok[x.first]==id){ xxj c=hb(f[now],f[x.first]);viss[x.second]=1; ans[x.second]=max(ans[x.second],query(c)); } } for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(!vis[to] and to!=fa) dfs(to,now); } } void upd(int now,int fa){ ok[now]=id; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(!vis[to] and to!=fa) upd(to,now); } } void solve(int now){ vis[now]=1;id=now;ok[now]=id; f[now].clear();f[now].ins(val[now]); for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(!vis[to]) dfs(to,now),upd(to,now); } for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(!vis[to]){ tot=sze[to];MX=1e9; getroot(to,0);solve(root); } } } signed main(){ n=getint(),m=getint(); for(int i=1;i<=n;i++) val[i]=getint(); for(int i=1;i<n;i++){ int x=getint(),y=getint(); add(x,y),add(y,x); } for(int i=1;i<=m;i++){ int x=getint(),y=getint(); if(x==y) ans[i]=val[x]; else v[x].pb(mp(y,i)),v[y].pb(mp(x,i)); } tot=n,MX=1e9;getroot(1,0); solve(root); for(int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }