码迷,mamicode.com
首页 > 其他好文 > 详细

【atc abc160F】F - Distributing Integers(树的拓扑序计数)

时间:2020-05-18 18:41:24      阅读:60      评论:0      收藏:0      [点我收藏+]

标签:细节   mat   为我   back   push   拓扑   begin   最小   main   

传送门

题意:
求以每个点为根节点的树的拓扑序计数。

思路:
是一个挺经典的问题。
我们考虑自下而上的树形\(dp\):假设我们当前点在\(u\),我们已经求出来了\(dp[v],v\in sons_u\)
现在要求\(u\)的方案数,我们考虑在\(u\)放上\(1\),然后剩下\(sz[u]-1\)个数生成一个排列依次给每个结点赋值。之后类似于一个可重集的排列问题,对于每个结点,我们要除以\(sz[v]!\),就相当于我们从\(sz[u]-1\)个数中拿出来一些数给\(v\)这颗子树赋值,不考虑顺序的情况下,这些数不同的方案数。接下来就考虑上顺序,乘以一个\(dp[v]\)就行。所以总的方案数为:

\[(sz[u] - 1)!\cdot \prod\frac{dp[i]}{sz[i]!},u\in father(i) \]

当然,还有更加简单的公式,我们不用考虑\(dp\),因为我们发现首位固定最小的排列数为\((n-1)!\)。那么对于以\(u\)为根节点的子树答案为:

\[sz[u]!\prod\frac{1}{sz[i]},u\in father(i) \]

这两个式子是等价的,我们可以直接归纳证明。

这样就求出来了以一个点作为根节点的情况,求所有点作为根结点的情况直接换根跑一下就行。
细节见代码:

/*
 * Author:  heyuhhh
 * Created Time:  2020/5/16 17:42:54
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ‘ ‘; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ‘ ‘; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5, MOD = 1e9 + 7;

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

int n;
vector <int> G[N];
int sz[N], f[N];

void dfs(int u, int fa) {
    sz[u] = 1;
    for (auto v : G[u]) if (v != fa) {
        dfs(v, u);
        sz[u] += sz[v];
    }
}

void dfs2(int u, int fa) {
    for (auto v : G[u]) if (v != fa) {
        f[v] = 1ll * f[u] * sz[v] % MOD * qpow(n - sz[v], MOD - 2) % MOD;
        dfs2(v, u);
    }
}

void run() {
    cin >> n;
    for (int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    f[1] = 1;
    for (int i = 1; i <= n; i++) {
        f[1] = 1ll * f[1] * i % MOD;
    }
    for (int i = 1; i <= n; i++) {
        f[1] = 1ll * f[1] * qpow(sz[i], MOD - 2) % MOD;
    }
    dfs2(1, 0);
    for (int i = 1; i <= n; i++) {
        cout << f[i] << ‘\n‘;   
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

【atc abc160F】F - Distributing Integers(树的拓扑序计数)

标签:细节   mat   为我   back   push   拓扑   begin   最小   main   

原文地址:https://www.cnblogs.com/heyuhhh/p/12912044.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!