[题解] [BZOJ5093] 图的价值

题面

题解

由题面, 我们可以得出这样一个 ** 式子

\[\displaystyle ans = \sum_{\forall G}\sum_{i=1}^{n}d_i^k \]

其中 \(d_i\) 代表 \(i\) 点的度数

这个式子意为, 对于任意的一个简单无向图, 把每个点的答案加上...

这个.... 似乎并不好算, 我们尝试对于某一个点 \(i\) , 把他在每个图中的贡献放到一起讨论看一看

首先, 度数不同肯定贡献不同, 我们要枚举贡献

那这个贡献会算多少次呢

注意到我们并不需要图联通, 只要没有自环和重边即可

那么对于其他的 \(n - 1\) 个点, 他们之间可以任意连边

所以我们就将上式转化为

\[\displaystyle ans = \sum_{i=1}^n\sum_{d=0}^{n-1}\binom{n-1}{d}d^k*2^{\frac{(n - 1)(n - 2)}{2}} \]

代表我们对于 \(i\) 点, 选 \(d\) 条边连出去, 有 \(n - 1\) 条边可选, 然后其他的点随便连

接着我们发现每一个点最后的贡献都是相同的, 这个枚举的 \(i\) 似乎并没有什么用

所以有

\[\displaystyle ans = n*2^{\frac{(n - 1)(n - 2)}{2}}\sum_{d=0}^{n-1}\binom{n-1}{d}d^k \]

后面这个式子似乎在哪里见过

然后按照上面的推导, 最后化出来是这个样子的

\[\displaystyle ans = n*2^{\frac{(n - 1)(n - 2)}{2}}\sum_{i=0}^{k}i!\begin{Bmatrix}k\\i\end{Bmatrix}\binom{n-1}{i}2^{n-i} \]

但是这个题中的 \(k\) 比较大, 这个第二类斯特林数有点难搞

有了!

我们发现第二类斯特林数有这样一个递推式

\[\displaystyle \begin{aligned}\begin{Bmatrix}n\\m\end{Bmatrix}&=\frac{1}{m!}\sum_{i=0}^{m}(-1)^i\binom{m}{i}(m-i)^n\\&=\frac{1}{m!}\sum_{i=0}^m(-1)^i\frac{m!}{i!(m-i)!}(m-i)^n\\&=\sum_{i=0}^m\frac{(-1)^i}{i!}*\frac{(m-i)^n}{(m-i)!}\end{aligned} \]

这是个卷积, NTT 求一下就好了

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int N = 200005;
const int mod = 998244353; 
using namespace std;
 
int n, m, k, len, lim, r[N << 2], a[N << 2], b[N << 2], g, gg, fac[N], inv[N], ans; 
 
template < typename T >
inline T read()
{
    T x = 0, w = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * w; 
}
 
int fpow(int x, int y)
{
    int res = 1;
    for( ; y; y >>= 1, x = 1ll * x * x % mod)
        if(y & 1) res = 1ll * res * x % mod;
    return res; 
}
 
void ntt(int *p, int opt)
{
    for(int i = 0; i < k; i++)
        if(i < r[i]) swap(p[i], p[r[i]]);
    for(int i = 1; i < k; i <<= 1)
    {
        int rt = fpow(opt == 1 ? g : gg, (mod - 1) / (i << 1));
        for(int j = 0; j < k; j += (i << 1))
        {
            int w = 1;
            for(int l = j; l < j + i; l++, w = 1ll * w * rt % mod)
            {
                int x = p[l], y = 1ll * w * p[l + i] % mod;
                p[l] = (x + y) % mod, p[l + i] = (x - y + mod) % mod; 
            }
        }
    }
}
 
void prepare1()
{
    g = 3, gg = fpow(g, mod - 2);
    for(int i = (fac[0] = 1); i <= k; i++)
        fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[k] = fpow(fac[k], mod - 2);
    for(int i = k - 1; i >= 0; i--)
        inv[i] = 1ll * inv[i + 1] * (i + 1) % mod; 
}
 
void prepare2()
{
    for(int i = 0; i <= k; i++)
        a[i] = 1ll * (i & 1 ? mod - 1 : 1) * inv[i] % mod;
    for(int i = 0; i <= k; i++)
        b[i] = 1ll * fpow(i, k) * inv[i] % mod;
    for(len = 2 * k, k = 1; k <= len; k <<= 1, lim++); lim--;
    for(int i = 0; i < k; i++)
        r[i] = (r[i >> 1] >> 1) | ((i & 1) << lim);
    ntt(a, 1), ntt(b, 1);
    for(int i = 0; i < k; i++)
        a[i] = 1ll * a[i] * b[i] % mod;
    ntt(a, -1);
    int tmp = fpow(k, mod - 2);
    for(int i = 0; i < k; i++)
        a[i] = 1ll * a[i] * tmp % mod; 
}
 
int main()
{
    n = read <int> (), k = read <int> (), m = min(n - 1, k);
    prepare1(), prepare2(); 
    for(int res = 1, i = 0; i <= m; res = 1ll * res * (n - i - 1) % mod, i++)
        ans = (ans + 1ll * res * a[i] % mod * fpow(2, n - i - 1) % mod) % mod;
    ans = 1ll * ans * n % mod * fpow(2, 1ll * (n - 1) * (n - 2) / 2 % (mod - 1)) % mod;
    printf("%d\n", ans); 
    return 0; 
}
posted @ 2020-01-17 22:00  ztlztl  阅读(136)  评论(0编辑  收藏  举报