C++ 知识点选讲 8——DFS 序(真的可以将图映射为序列吗?)

学了这个就跳着讲吧。

个人认为是橙,但是它经常和一些别的东西混用。比如下面这道题:

有 \(q\) 组询问,每次询问有两种操作:

\(1 \ u \ x\):把以节点 \(u\) 为根的子树中所有点的权值都加上 \(x\)
\(2 \ u\):查询节点 \(u\) 的权值

如果 \(q \le 10^5\) 或者更大,暴力法就不行了。DFS 序可以将其映射为数列,再配合树状数组或线段树解决这个问题。

先来讲讲如何实现。

维护一个时间戳 \(id\),声明两个数组 \(in\) 和 \(out\)。当 DFS 搜索到一个节点时(设这个节点为 \(now\)),将 \(in_{now}\) 赋值为 \(id\),随后遍历每一个子节点。临回溯时将 \(out_{now}\) 赋值为 \(id\)。

模板:

void dfs(int x,int v){
    fa[x]=v;
    dfn[x]=++id;
    siz[x]=1;
    dep[x]=dep[v]+1;
    for(int i=0; i<G[x].size(); i++){
        int tx=G[x][i];
        if(tx!=v){
            dfs(tx,x);
            siz[x]+=siz[tx];
        }
    }
    out[x]=id;
}

(如上程序所示,一次 dfs 可以统计许多东西)

打表输出每个节点的 \(in\) 和 \(out\),能发现一个性质——节点 \(now\) 的子树中所有节点的 \(in\) 值,恰好构成一个连续区间 \([in_u,out_u]\),为我们映射提供有利条件。

再配上树状数组或线段树,上面的问题可以很巧妙地解决。因为操作 \(1\) 就是树状数组的区间加,操作 \(2\) 就是树状数组单点查询。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
vector<int> G[50005];
int P[50005];
int id,a[50005],n,in[50005],out[50005];
void dfs(int x,int v){
    // fa[x]=v;
    in[x]=++id;
    for(int i=0; i<G[x].size(); i++){
        int tx=G[x][i];
        if(tx!=v){
            dfs(tx,x);
        }
    }
    out[x]=id;
}
int T[50005];
int lb(int x){
    return x&(-x);
}
void Add(int x,int k){
    while(x<=n){
        T[x]+=k;
        x+=lb(x);
    }
}
int Q(int x){
    int sum=0;
    while(x>=1){
        sum+=T[x];
        x-=lb(x);
    }
    return sum;
}
signed main(){
    int q;
    scanf("%lld %lld",&n,&q);
    for(int i=1; i<=n; i++){
        scanf("%lld",&a[i]);
        // Add(in[i],a[in[i]]);
    }
    for(int i=1; i<n; i++){
        int u,v;
        scanf("%lld %lld",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,0);
    for(int i=1; i<=n; i++){
        P[in[i]]=a[i];
    }
    for(int i=1; i<=n; i++) Add(i,P[i]-P[i-1]);
    while(q--){
        int op;
        scanf("%lld",&op);
        if(op==1){
            int l,r;
            scanf("%lld %lld",&l,&r);
            Add(in[l],r),Add(out[l]+1,-r);
        }else{
            int l;
            scanf("%lld",&l);
            printf("%lld\n",Q(in[l]));
        }
    }
    return 0;
}

这是 DFS 序最简单的应用。

posted @ 2026-07-12 20:40  lz5332t  阅读(11)  评论(1)    收藏  举报