小蒟蒻初学长链剖分的第一题
题意:给出一棵树,每次求 x x x的 k k k次祖先,强制在线。 ( n ≤ 300000 , q ≤ 1800000 ) (n\leq 300000,q\leq 1800000) (n≤300000,q≤1800000)
这题 O ( n l o g n + q l o g n ) O(n\ log\ n+q\ log\ n) O(n log n+q log n)的倍增是过不去的,考虑长链剖分。
首先要知道,长链剖分相对于重链剖分,区别就在于前者以最大深度最大的那一棵子树为重儿子,后者以 s i z e size size最大的那一棵子树为重儿子。
长链剖分完后,树就被划分成一条条重链(也可以说叫长链)和一堆轻边。
为什么要长链剖分呢?我们发现它有一些比较优美的性质:
1.一个点向上跳到根,只会经过不超过 n \sqrt{n} n条重链。大概理解一下就像这张图
首先每次向上跳到的非当前重链的链,肯定比当前重链长,那么最坏就是像上图一样,向上跳时边长 1 , 2 , 3 , 4... 1,2,3,4... 1,2,3,4...递增,最多有 n \sqrt{n} n条。
2.一个点 x x x的 k k k次祖先 y y y所在重链长度大于等于 k k k。这也很好理解,假设长度小于 k k k,那么显然 y − x y-x y−x这条链更长,与假设矛盾。
3.所有重链长度加起来是 O ( n ) O(n) O(n)级的。每个点最多延伸出去一条重边,显然结论成立。
那么我们来做这道题。
对于求一个点 x x x的 k k k次祖先,我们先跳到 x x x的 r r r次祖先 y y y,并且保证 k ≤ 2 r k\leq 2r k≤2r,再求 y y y的 k − r k-r k−r次祖先。显然这个 r r r的值我们可以取 h i g h b i t ( k ) highbit(k) highbit(k),所以 r r r是 2 2 2的幂次,那么我们可以直接倍增预处理 x x x的 r r r次祖先。
由之前我们得到的结论, x x x的 r r r次祖先 y y y所在的重链的长度大于等于 r r r,因为我们保证了 k ≤ 2 r k\leq 2r k≤2r,所以有 k − r ≤ r k-r\leq r k−r≤r。那么 y y y的 k − r k-r k−r次祖先一定在 y y y所在重链的链顶向上向下各走链长步的范围内。
于是我们对于每条链,预处理链顶向上向下各走链长步能走到的所有点。因为由上面的结论 3 3 3,所以预处理这个东西的复杂度为 O ( n ) O(n) O(n)。
至此我们可以 O ( 1 ) O(1) O(1)求 x x x的 k k k次祖先了。
总复杂度就是 O ( n l o g n + q ) O(n\ log\ n+q) O(n log n+q)。
C o d e B e l o w : Code\ Below: Code Below:
#include<bits/stdc++.h>
#define ts cout<<"ok"<<endl
#define ll long long
#define hh puts("")
#define pc putchar
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
using namespace std;
const int N=300005;
int n,m,head[N],cnt,son[N],top[N],bz[N][20],dep[N],mx[N],len[N],hb[N],mi[20];
struct Edge{
int v,nx;
}e[N<<1];
vector<int> up[N],down[N];
inline int read(){
int ret=0,ff=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') ff=-ff;ch=getchar();}
while(isdigit(ch)){ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*ff;
}
void write(int x){
if(x<0){x=-x;putchar('-');}
if(x>9) write(x/10);
putchar(x%10+48);
}
void writeln(int x){write(x),hh;}
void add(int x,int y){
e[++cnt].v=y;
e[cnt].nx=head[x];
head[x]=cnt;
}
void dfs1(int now,int fa){
dep[now]=mx[now]=dep[fa]+1;
bz[now][0]=fa;
for(int i=1;i<=19;i++) bz[now][i]=bz[bz[now][i-1]][i-1];
for(int i=head[now];i;i=e[i].nx){
int v=e[i].v;
if(v==fa) continue;
dfs1(v,now);
if(mx[v]>mx[now]) mx[now]=mx[v],son[now]=v;
}
}
void dfs2(int now,int fa){
len[now]=mx[now]-dep[top[now]]+1;
if(!son[now]) return;
top[son[now]]=top[now],dfs2(son[now],now);
for(int i=head[now];i;i=e[i].nx){
int v=e[i].v;
if(v==fa||v==son[now]) continue;
top[v]=v,dfs2(v,now);
}
}
int query(int x,int k){
if(dep[x]<=k) return 0;
if(!k) return x;
x=bz[x][hb[k]],k^=mi[hb[k]];
if(!k) return x;
int l=dep[x]-dep[top[x]];
if(k==l) return top[x];
if(k>l) return up[top[x]][k-l-1];
return down[top[x]][l-k-1];
}
signed main(){
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y),add(y,x);
}
dfs1(1,0);
top[1]=1;
dfs2(1,0);
for(int i=1;i<=n;i++){
if(i!=top[i]) continue;//只处理链顶
int l=len[i],now=i;
while(l--&&now) now=bz[now][0],up[i].push_back(now);
l=len[i],now=i;
while(l--) now=son[now],down[i].push_back(now);
}
mi[0]=1;
for(int i=1;i<=19;i++) mi[i]=1<<i;
for(int i=1;i<=n;i++)
for(int j=19;j>=0;j--)
if(i&(1<<j)){//highbit(i)*2>i
hb[i]=j;
break;
}
m=read();
int ans=0;
while(m--){
int x=read()^ans,k=read()^ans;
ans=query(x,k);
writeln(ans);
}
return 0;
}