• 【算法题解】拓扑序计数+树形DP


    拓扑序计数+树形DP

    题目

    链接:https://ac.nowcoder.com/acm/contest/38630/F

    image-20220904192158381

    思路

    每个公司是一棵树,有n家公司,可以将这n家公司连到一个虚拟的根上。总共的排队方案就等于这个棵的排队方案树。为了满足排队是顺序的,所以我们要求的就是这棵树的拓扑序个数。用树形DP来求解。

    f[u]: 以u为根的子树的拓扑序数

    sz[u]: 以u为根的子树的大小(节点的数量)

    如何计算一个棵的拓扑序数?

    我们先来看只有两个子树的情况:

    image-20220904195600066

    如上图所示,1号节点一定是第1为,那么剩下还有3位置,有三种可能:

    • 1 2 3 4
    • 1 3 2 4
    • 1 2 4 3

    这就用到了概率论的知识,有三个盒子,从中要选2个盒子给2、4放置,并且是按照顺序的2 4放置,因为是拓扑序,所以每个子树的相对顺序是一定的,所以最后计算的结果就是:
    f [ u ] = f [ v 1 ] ⋅ f [ v 2 ] ⋅ C ( s z [ v 1 ] + s z [ v 2 ] , s z [ v 1 ] ) f[u]=f[v1]⋅f[v2]⋅C(sz[v1]+sz[v2],sz[v1]) f[u]=f[v1]f[v2]C(sz[v1]+sz[v2],sz[v1])
    当树为二叉树时,将两个子树v1,v2进行合并:即先把各子树的方案数乘起来算出总方案,然后考虑各子树元素的相对排列顺序,即在总的节点个数中选sz[v1]sz[v1]个位置,剩下的顺序就固定了,保证每颗子树的相对拓扑序不变。

    image-20220904200620793

    如何计算组合数

    在这里插入图片描述

    代码

    #include 
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> PII;
    typedef priority_queue<int, vector<int>, less<int>> Q;
    #define x first
    #define y second
    #define endl '\n'
    #define ppb pop_back
    #define pb push_back
    #define pf push_front
    #define YES cout << "YES" << endl
    #define Yes cout << "Yes" << endl
    #define yes cout << "yes" << endl
    #define NO cout << "NO" << endl
    #define No cout << "No" << endl
    #define no cout << "no" << endl
    #define all(x) x.begin(), x.end()
    #define rall(x) x.rbegin(), x.rend()
    #define mset(x, a) memset(x, a, sizeof(x))
    #define rep(i, l, r) for (LL i = l; i <= (r); ++i)
    #define per(i, r, l) for (LL i = r; i >= (l); --i)
    const int N = 1e5 + 10, inf = 0x3f3f3f3f, mod = 1e9 + 7;
    vector<int> v[N];
    int n;
    int sz[N];
    ll fac[N];
    ll inv[N];
    ll f[N];
    ll qsm(ll a, ll b)
    {
        ll ans = 1;
        while (b)
        {
            if (b & 1)
                ans = ans * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return ans;
    }
    void dfs(int u)
    {
        f[u] = 1;
        sz[u] = 1;
        for (int i = 0; i < v[u].size(); i++)
        {
            int j = v[u][i];
            dfs(j);
            sz[u] += sz[j];
            f[u] = (f[u] * f[j]) % mod * inv[sz[j]] % mod;
        }
        f[u] = f[u] * fac[sz[u] - 1] % mod;
    }
    void solve()
    {
        fac[0] = 1;
        inv[0] = 1;
        for (int i = 1; i < N; i++)
        {
            fac[i] = fac[i - 1] * i % mod;
            inv[i] = inv[i - 1] * qsm(i, mod - 2) % mod;
        }
        cin >> n;
        ll ans = 1;
        int cnt = 0;
        for (int i = 1; i <= n; i++)
        {
            int c;
            cin >> c;
            v[0].pb(cnt + 1);
            for (int j = 2; j <= c; j++)
            {
                int u;
                cin >> u;
                v[cnt + u].pb(cnt + j);
            }
            cnt += c;
        }
        dfs(0);
        cout << f[0];
    }
    signed main()
    {
    #ifdef Xin
        freopen("in.in", "r", stdin);
        freopen("out.out", "w", stdout);
    #endif
        int T = 1;
        while (T--)
            solve();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    感谢大佬的文章:

    • http://wyqz.top/p/2852940489.html
    • https://www.acwing.com/solution/content/16482/
  • 相关阅读:
    三、java基础语法
    java并发编程
    Js手写面试题5-Promise
    Sui主网升级至V1.12.2版本
    【Python】sort 排序
    【布局优化】基于粒子群算法求解集线器位置分配问题附matlab代码
    关于Conversational QA 的一些调研
    服务器编程基本框架
    JAVA入门——方法引用
    语音转文字以及音频格式转换(推荐链接)
  • 原文地址:https://blog.csdn.net/weixin_53029342/article/details/126693982