题解 vijos lxhgww的奇思妙想

传送门

小蒟蒻初学长链剖分的第一题

题意:给出一棵树,每次求 x x x k k k次祖先,强制在线。 ( n ≤ 300000 , q ≤ 1800000 ) (n\leq 300000,q\leq 1800000) (n300000,q1800000)

这题 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 yx这条链更长,与假设矛盾。

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 k2r,再求 y y y k − r k-r kr次祖先。显然这个 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 k2r,所以有 k − r ≤ r k-r\leq r krr。那么 y y y k − r k-r kr次祖先一定在 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值