• AtCoder NOMURA 2020 D


    首先, 一种情况下的答案就是 (n~-) 联通块数.
    要算所有情况下联通块数的和, 可以对于每个联通块单独考虑它出现的方案数再加起来.
    于是我们可以对于每个联通块选择一个 代表点, 对于这些代表点计数即可.
    将原图除 (-1) 之外的边连上, 将会是基环内向树和树的混合森林, 我们可以将基环树的环作为代表点.
    注意到树的根节点的 (p)(-1), 而基环树没有 (p)(-1) 的节点.
    先对基环树统计方案数. 考虑到它不可能再向外连边, 那么它成为连通块代表点的方案就是 ((N-1)^K).
    然后对含有 -1 的树结构计数: 考虑最终一个连通块中的环, 假设它是由大小为 (a_1,a_2,dots,a_m) 的树连成的, 那么它对答案的贡献就是 ((prod{a_i})(i-1)!(N-1)^{K-i}) (注意当 (m=1) 时贡献为 ((a_i-1)(N-1)^{K-1})). 由于一共有 (K) 个这样的树, 所以这部分的贡献要用 DP 来算.
    最终将两部分贡献加起来就是所有关键点(即所有连通块)贡献, 用 (N(N-1)^K) 减一下就是答案.

    #include <bits/stdc++.h>
    #define N 5050
    using namespace std;
    const int mod = 1e9 + 7;
    
    int fsp(int x, int k = mod - 2) {
        int res = 1;
        while (k) {
            if (k & 1) res = 1ll * res * x % mod;
            x = 1ll * x * x % mod, k >>= 1;
        }
        return res;
    }
    
    int n, p[N], sz[N], f[N][N], vis[N], t;
    vector<int> G[N];
    
    void dfs(int u, int r) {
        vis[u] = 1;
        if (r < 0) sz[-r]++;
        for (int v : G[u])
            if (v != r) dfs(v, r);
            else ++t;
    }
    
    int main() {
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", p + i);
            if (~p[i]) G[p[i]].push_back(i);
        }
    
        int K = 0;
        for (int i = 1; i <= n; ++i)
            if (p[i] == -1) dfs(i, -(++K));
        for (int i = 1; i <= n; ++i)
            if (!vis[i]) dfs(i, i);
    
        int ans = 1ll * t * fsp(n - 1, K) % mod;
    
        f[0][0] = 1;
        for (int i = 1; i <= K; ++i) {
            f[i][0] = 1;
            for (int j = 1; j <= i; ++j)
                f[i][j] = (f[i - 1][j] + 1ll * f[i - 1][j - 1] * sz[i]) % mod;
        }
    
        if (K) ans = (ans + 1ll * (f[K][1] - K) * fsp(n - 1, K - 1)) % mod;
        for (int i = 2, j = 1; i <= K; ++i) {
            j = 1ll * j * (i - 1) % mod;
            ans = (ans + 1ll * f[K][i] * j % mod * fsp(n - 1, K - i)) % mod;
        }
    
        ans = (1ll * n * fsp(n - 1, K) - ans) % mod;
        if (ans < 0) ans += mod;
        cout << ans << endl;
    
        return 0;
    }
    
  • 相关阅读:
    layui 参照赋值的两种方式
    c笔记
    Linux操作系统笔记
    make笔记
    Gcc如何知道文件类型。
    #include <xxx.h>和#include "xxx.h"的区别
    GCC编译流程
    c++ Socket客户端和服务端示例版本三(多线程版本)
    c++ Socket客户端和服务端示例版本二
    c++ Socket客户端和服务端示例版本一
  • 原文地址:https://www.cnblogs.com/hnfms-jerry/p/solution_ATC_NOMURA2020_D.html
Copyright © 2020-2023  润新知