[校内训练]无向图(第二类斯特林数)

problem

  • 定义一个点的权值为它的度数的 \(m\) 次方,规定 \(0^0=1\)
  • 对于一张无向图,它的权值是所有点的权值和。
  • 求所有 \(n\) 个点的无向图(共有 \(2^{C_n^2}\) 种)的权值之和,对 \(998244353\) 取模。
  • \(1≤n≤10^9,0≤m≤5*10^5\)

Solution

  • 分别考虑每个点对答案的贡献:固定一个点 \(u\),枚举 \(u\) 的度数 \(i\),对答案的贡献为 \(i^m\)。而满足 \(u\) 度数为 \(i\) 的图中,另外 \(n-1\) 个点要选出 \(i\) 个点跟 \(u\) 连边(方案数:\(C_{n-1}^i\))。然后另外 \(n-1\) 个点两两之间可以随意连边(方案数:\(2^{C_{n-1}^2}\))。而 \(u\) 可以是 \(1\) ~ \(n\) 的任意一个点,所以有:
    \[ans=n*2^{C_{n-1}^2}*\sum_{i=0}^{n-1}i^m*C_{n-1}^i\]
  • 显然我们只要求:
    \[sum=\sum_{i=0}^{n-1}i^m*C_{n-1}^i\]
  • 众所周知:
    \[i^m=\sum_{j=0}^{m}S(m,j)*C_i^j*j!\]
  • 那么有:
    \[sum=\sum_{j=0}^{m}S(m,j)*j!*\sum_{i=0}^{n-1}C_i^j*C_{n-1}^i\]

  • 考虑 \(C_i^j*C_{n-1}^i\) 的组合意义:\(n-1\) 个带编号的球里面选出 \(i\) 个涂成蓝色,再在这 \(i\) 个里面选出 \(j\) 个涂成红色。这等价于:\(n-1\) 个带编号的球里面选出 \(j\) 个涂成红色,再在剩下的 \(n-j-1\) 个里面选出 \(i-j\) 个涂成蓝色。
  • 那么有:
    \[sum=\sum_{j=0}^{m}S(m,j)*j!*C_{n-1}^j*\sum_{i=0}^{n-1}C_{n-j-1}^{i-j}\]

  • 即:
    \[sum=\sum_{j=0}^{m}S(m,j)*j!*C_{n-1}^j*2^{n-j-1}\]

  • 众所周知:
    \[S(m,j)=\frac{1}{j!}\sum_{i=0}^{j}(-1)^i*(j-i)^n*C_j^i\]

  • 那么我们可以用 \(NTT\) 求出每个 \(S(m,j)\),时间复杂度 \(O(m \log m)\)

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
    char ch;
    while (ch = getchar(), !isdigit(ch));
    res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
    res = res * 10 + (ch ^ 48);
}

const int mod = 998244353, e = 2e6 + 5;
int n, m, lim, rev[e], g[e], h[e], f[e], fac[e], inv[e], ans;

inline int ksm(int x, ll y)
{
    int res = 1;
    while (y)
    {
        if (y & 1) res = (ll)res * x % mod;
        y >>= 1;
        x = (ll)x * x % mod;
    }
    return res;
}

inline void upt(int &x, int y)
{
    x = y;
    if (x >= mod) x -= mod;
}

inline void fft(int n, int *a, int op)
{
    int i, j, k, r = (op == 1 ? 3 : (mod + 1) / 3);
    for (i = 0; i < n; i++)
    if (i < rev[i]) swap(a[i], a[rev[i]]);
    for (k = 1; k < n; k <<= 1)
    {
        int w0 = ksm(r, (mod - 1) / (k << 1));
        for (i = 0; i < n; i += (k << 1))
        {
            int w = 1;
            for (j = 0; j < k; j++)
            {
                int b = a[i + j], c = (ll)w * a[i + j + k] % mod;
                upt(a[i + j], b + c);
                upt(a[i + j + k], b + mod - c);
                w = (ll)w * w0 % mod;
            }
        }
    }
}

int main()
{
    freopen("graph.in", "r", stdin);
    freopen("graph.out", "w", stdout);
    cin >> n >> m;
    lim = 1;
    int i, k = 0;
    while (lim < m * 2 + 5)
    {
        lim <<= 1;
        k++;
    }
    fac[0] = 1;
    for (i = 1; i <= m; i++) fac[i] = (ll)fac[i - 1] * i % mod;
    inv[m] = ksm(fac[m], mod - 2);
    for (i = m - 1; i >= 0; i--) inv[i] = (ll)inv[i + 1] * (i + 1) % mod;
    for (i = 0; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << k - 1);
    for (i = 0; i <= m; i++)
    {
        if (i & 1) g[i] = mod - 1;
        else g[i] = 1;
        g[i] = (ll)g[i] * inv[i] % mod;
        h[i] = (ll)inv[i] * ksm(i, m) % mod;
    }
    fft(lim, g, 1);
    fft(lim, h, 1);
    for (i = 0; i < lim; i++) f[i] = (ll)g[i] * h[i] % mod;
    fft(lim, f, -1);
    int tot = ksm(lim, mod - 2);
    for (i = 0; i < lim; i++) f[i] = (ll)f[i] * tot % mod;
    int c = 1;
    for (i = 0; i <= m; i++)
    if (n - 1 >= i)
    {
        ans = (ans + (ll)f[i] * fac[i] % mod * c % mod * ksm(2, n - 1 - i)) % mod;
        c = (ll)c * ksm(i + 1, mod - 2) % mod * (ll)(n - 1 - i) % mod;
    }
    else break;
    ans = (ll)ans * n % mod * ksm(2, (ll)(n - 1) * (n - 2) / 2) % mod;
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cyf32768/p/12196466.html