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

简介: 【算法题解】拓扑序计数+树形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[v1]⋅f[v2]⋅C(sz[v1]+sz[v2],sz[v1]) $$

当树为二叉树时,将两个子树v1,v2进行合并:即先把各子树的方案数乘起来算出总方案,然后考虑各子树元素的相对排列顺序,即在总的节点个数中选sz[v1]sz[v1]个位置,剩下的顺序就固定了,保证每颗子树的相对拓扑序不变。

image-20220904200620793

如何计算组合数

在这里插入图片描述

代码

#include <bits/stdc++.h>
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;
}

感谢大佬的文章:

相关文章
|
18天前
|
机器学习/深度学习 数据采集 算法
Python用逻辑回归、决策树、SVM、XGBoost 算法机器学习预测用户信贷行为数据分析报告
Python用逻辑回归、决策树、SVM、XGBoost 算法机器学习预测用户信贷行为数据分析报告
|
3天前
|
算法 C++ 索引
分享一个关于Avl树的迭代器算法
该文介绍了无parent指针的AVL树迭代实现,提出了一种仅使用少量栈空间的双向迭代算法。算法分为初始化、前向迭代和后向迭代三部分。初始化时,根据起始点(最小或最大值)填充栈,然后根据状态进行前向或后向迭代。前向迭代检查当前节点的右子节点,后向迭代检查左子节点。算法通过堆栈维持双向迭代,解决了节点丢失和失序问题。此外,还讨论了算法在多线程环境下的同步问题和可能的解决方案。
|
4天前
|
机器学习/深度学习 算法 API
【机器学习】Python中的决策树算法探索
决策树作为机器学习中的一种基础且强大的算法,因其易于理解和实现、能够处理分类和回归任务的特性而广受欢迎。本文旨在深入浅出地介绍决策树算法的基本原理,并通过Python编程语言实践其应用,帮助读者掌握如何利用Python构建及优化决策树模型。本文预计分为以下几个部分:决策树基础理论、Python中实现决策树的库介绍、实战案例分析、模型评估与调优方法,以及决策树算法的局限性与未来展望。
9 0
|
9天前
|
存储 算法 C++
c++算法学习笔记 (8) 树与图部分
c++算法学习笔记 (8) 树与图部分
|
13天前
|
存储 移动开发 算法
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构(下)
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构
26 0
|
13天前
|
机器学习/深度学习 算法 搜索推荐
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构(上)
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构
13 0
|
15天前
|
机器学习/深度学习 算法 数据处理
探索机器学习中的决策树算法
【5月更文挑战第18天】探索机器学习中的决策树算法,一种基于树形结构的监督学习,常用于分类和回归。算法通过递归划分数据,选择最优特征以提高子集纯净度。优点包括直观、高效、健壮和可解释,但易过拟合、对连续数据处理不佳且不稳定。广泛应用于信贷风险评估、医疗诊断和商品推荐等领域。优化方法包括集成学习、特征工程、剪枝策略和参数调优。
|
18天前
|
机器学习/深度学习 算法
理解并应用机器学习算法:决策树
【5月更文挑战第12天】决策树是直观的分类与回归机器学习算法,通过树状结构模拟决策过程。每个内部节点代表特征属性,分支代表属性取值,叶子节点代表类别。构建过程包括特征选择(如信息增益、基尼指数等)、决策树生成和剪枝(预剪枝和后剪枝)以防止过拟合。广泛应用在信贷风险评估、医疗诊断等领域。理解并掌握决策树有助于解决实际问题。
|
18天前
|
存储 缓存 算法
数据结构与算法 树(B树,B+树,红黑树待完善)
数据结构与算法 树(B树,B+树,红黑树待完善)
22 0
|
18天前
|
机器学习/深度学习 算法 数据可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化