P2633 Count on a tree 题解(外加DFS序求LCA)

发布时间 2023-09-08 10:56:25作者: NBest

2023-07-22 09:53:59 顶置3

P2633 Count on a tree

前置小知识

冷门小科技:DFS-RMQ 求LCA

最近跟着洛谷榜一的博客学了一个冷门科技:DFS序求LCA,这道题刚好要求LCA,所以就刚好适用一下。

\(\color{Red}{原博客地址}\)

DFS序求LCA原理:
我们令u点在dfs序中出现的位置为时间戳 \(dfn[u]\)

\(dfn[u]\) < \(dfn[v]\)

当u不是v的祖先时,设d为\(lca(u,v)\)

观察到\(dfn[d]\)< \(dfn[u]\) < \(dfn[v]\),与欧拉序的祖先在u,v之间出现过不同。

但是我们找\(d\)\(v\)方向的第一个节点\(v'\),因为 \(dfn[u]\) < \(dfn[v]\),所以\(dfn[v']\)属于[ \(dfn[u]\) , \(dfn[v]\)](想一下就知道了, \(dfn[v]\)是最大的)。

那么可以得到[ \(dfn[u]\) , \(dfn[v]\)]中深度最小的节点的父亲即为d。

二:对于u是v祖先的情况,我们显然不能查 \(dfn[u]\) 了,所以我们可以查[ \(dfn[u]\) +1, \(dfn[v]\)]中深度最小的元素的最大值。

这样查对情况一,显然可以。

我们用一个倍增数组 \(fa[k][x]\) 表示从dfn为\([x,x+(1<<k)-1]\)的节点中父亲dfn最小的节点的父亲。

那对于dfn为区间\([ dfn[u] +1, dfn[v] ]\)中父亲dfn最小的节点的父亲即为 \(lca(u,v)\)

\(Code\)

int n,m,st,fa[20][500005],dfn[500005],dn;//fa[k][x]表示dfn从[x,x+(1<<k)-1]的节点中父亲dfn最小的节点的父亲
vector<int>f[500005];//                    对于u,v两个数来说,[dfn[u]+1,dfn[v]]中父亲dfn最小的即为LCA。
inline int get(int x,int y){return dfn[x]<dfn[y]?x:y;}
void dfs(int x,int Fa){
    fa[0][dfn[x]=++dn]=Fa;
    for(int y:f[x])if(y!=Fa)dfs(y,x);
}
int lca(int x,int y){
    if(x==y)return x;
    if((x=dfn[x])>(y=dfn[y]))x^=y^=x^=y;
    int k=__lg(y-x++);
    return get(fa[k][x],fa[k][y-(1<<k)+1]);
}
int main(){
    n=read(),m=read(),st=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        f[u].push_back(v),f[v].push_back(u);
    }
    dfs(st,0);
    for(int k=1;k<=__lg(n);k++)
        for(int i=1;i+(1<<k)-1<=n;i++)
            fa[k][i]=get(fa[k-1][i],fa[k-1][i+(1<<k-1)]);
    while(m--){
        printf("%d\n",lca(read(),read()));
    }
}

那开始讲正题吧

因为是第k小值,我们考虑到之前做的权值可持久化线段树模板,

准备将根节点的父亲的树设置为原始树(空树),之后的树都是在原始树的基础上增加节点,实现单点修改。

然后在算排名时,我们只需要 \(x=siz[u->root]+siz[v->root]-siz[lca(u,v)->root]-siz[fa(lca(u,v))->root]\) 即可。

其他细节和模板题一样,离散,建树都一样,复杂度也一样。

P.S. 写这道题的时候真的非常的爽,没看题解,花25分钟写完微调一下就过了。应该是我写过最爽的题之一了。
(还有应该是之前写矩阵加速优化状态压缩dp)

\(Code\)

#define mid (l+r>>1)
struct tree{
    int l,r,v;
}tr[5000005];
int n,m,q,a[100005],b[100005],root[100005],cnt;
int fa[23][100005],dfn[100005],dn,ans;
vector<int>f[100005];
void build(int &root,int l,int r){
    root=++cnt;
    if(l==r)return;
    build(tr[root].l,l,mid),build(tr[root].r,mid+1,r);
}
void update(int &root,int now,int l,int r,int pos){
    root=++cnt,tr[root]=tr[now],tr[root].v++;
    if(l==r)return;
    if(mid>=pos)update(tr[root].l,tr[now].l,l,mid,pos);
    else update(tr[root].r,tr[now].r,mid+1,r,pos);
}
int query(int u,int v,int lca,int lcafa,int l,int r,int k){
    if(l==r)return a[l];
    int x=tr[tr[u].l].v+tr[tr[v].l].v-tr[tr[lca].l].v-tr[tr[lcafa].l].v;
    if(x>=k)return query(tr[u].l,tr[v].l,tr[lca].l,tr[lcafa].l,l,mid,k);
    else return query(tr[u].r,tr[v].r,tr[lca].r,tr[lcafa].r,mid+1,r,k-x);
}
void dfs(int x,int Fa){
    fa[0][dfn[x]=++dn]=Fa;
    int o=lower_bound(a+1,a+1+n,b[x])-a;
    update(root[x],root[Fa],1,n,o);
    for(int y:f[x])if(y!=Fa)dfs(y,x);
}
inline int get(int x,int y){return dfn[x]<dfn[y]?x:y;}
int LCA(int x,int y){
    if(x==y)return x;
    if((x=dfn[x])>(y=dfn[y]))x^=y^=x^=y;
    int k=__lg(y-x++);//[dfn[u]+1,dfn[y]],所以这里要加一
    return get(fa[k][x],fa[k][y-(1<<k)+1]);
}
int main(){
    m=read(),q=read();
    for(int i=1;i<=m;i++){
        a[i]=b[i]=read();
    }
    sort(a+1,a+1+m);
    n=unique(a+1,a+1+m)-a-1;
    build(root[0],1,n);
    f[0].push_back(1);
    for(int i=1;i<m;i++){
        int u=read(),v=read();
        f[u].push_back(v),f[v].push_back(u);
    }
    dfs(1,0);
    for(int k=1;k<=__lg(m);k++)
        for(int i=1;i+(1<<k)-1<=m;i++)
            fa[k][i]=get(fa[k-1][i],fa[k-1][i+(1<<k-1)]);
    while(q--){
        int u=read()^ans,v=read(),k=read();
        int lca=LCA(u,v);
        ans=query(root[u],root[v],root[lca],root[fa[0][dfn[lca]]],1,n,k);
        printf("%d\n",ans);
    }
}