9 收心赛1 T2 深度优先搜索树

深度优先搜索树

题面

给定 \(dfs\) 代码

void dfs(int u) {
    vis[u] = true;
    for (int v = 1; v <= n; v++)
        if (g[u][v] == true && vis[v] == false)
            dfs(v), link(u, v);
}

给定一个 \(n\) 个点的树,问有多少个没有重边和自环的图 \(G\) 满足执行 \(dfs(1)\) 后生成的树和给定的树相同?

\(link\) 表示连边,\([g(u,v)]\) 表示 \(u,v\) 之间有无向边

\(n \le 2 \times 10^5\)

题解

感觉这道题其实思路上比上个题还简单些,考场上写挂了是因为没有考虑到 \(dfs\) 在一条链的情况下会退化成 \(O(n^2)\) ,后面要注意

好了,看完题手模几个小样例应该大概知道怎么连边

原树上的边一定要有,考虑其他的边

image-20250824185152854

假设两个点没有祖先关系(如 4和3),那么能不能连边?不能,因为如果连边,那么就不会遍历原有的树边,而是顺着新连的边 dfs 到 3,这样显然是不符合题意的

那么考虑有祖先关系的两个点如何连边?

如上图中的 6,7 连边可以吗,模拟发现不行,因为如果的靠下的节点(6)比靠上节点的子节点编号小,那么就会破坏原来的遍历顺序

所以两个点可以连边,当且仅当

两个点满足祖先关系并且靠下点编号大于靠上点子节点的编号

考场上我的思路是统计每个子树内根节点可以向那些节点连边,但是不好做(mtx用权值线段树+合并写出来了\bx),而且时间复杂度比较高

正难则反,考虑对于一个下面的节点,我们统计那些节点可以向它连边

但是直接去找那个靠上的点好像不太好找,我们发现连边条件和靠上节点的编号没有关系,所以我们可以转而去统计那个子节点,这个子节点要满足编号小于当前节点,并且不能是根节点(要有父亲)

可以用树状数组统计这个点到根的路径上的点的分布情况,然后 \(\log n\) 查询即可

每次时间复杂度为 \(O(\log n)\) 总时间复杂度为 \(O(n \log n)\)

code

代码比较简单,注意答案可能爆 \(int\)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10;
const int mod = 1e9 + 7;

int n;
int t[N];
int h[N], ver[N << 1], ne[N << 1], tot;
ll ans = 0;

void add (int x, int y) {
    ver[++tot] = y;
    ne[tot] = h[x];
    h[x] = tot;
}

int pw (int a, ll b) {
    ll res = 1, t = a;
    while (b) {
        if (b & 1) {
            res *= t;
            res %= mod;
        }
        t *= t;
        t %= mod;
        b >>= 1;
    }
    return res;
}

void Add (int x, int d) {
    while (x < N) {
        t[x] += d;
        x += x & -x;
    }
}

int ask (int x) {
    int res = 0;
    while (x) {
        res += t[x];
        x -= x & -x;
    }
    return res;
}

void dfs (int x, int fa) {
    Add (x, 1);
    //这里 -1 是因为根节点 1 一定会被统计上,而它必定没有父节点,所以不能作为
    if (fa) ans += ask (x - 1) - 1;
    for (int i = h[x]; i; i = ne[i]) {
        int y = ver[i];
        if (y == fa) continue;
        dfs (y, x);
    }
    Add (x, -1);
}

int main () {

    freopen ("dfs.in", "r", stdin);
    freopen ("dfs.out", "w", stdout);

    cin >> n;
    for (int i = 1; i < n; i++) {
        int x, y;
        scanf ("%d%d", &x, &y);
        add (x, y);
        add (y, x);
    }

    dfs (1, 0);
    cout << pw (2, ans) << endl;

    return 0;
}
posted @ 2025-08-28 14:14  michaele  阅读(23)  评论(0)    收藏  举报