bzoj5093 图的价值

题面bzoj5093

题意:定义一个带标号的图的价值为每个点度数的 k k k次方的和 mod 998244353 998244353 998244353。求 n n n个点带标号的简单无向图的价值和。 n ≤ 1 0 9 n \le 10^9 n109 k ≤ 200000 k \le 200000 k200000

题解:枚举每个点连多少条边: ∑ i = 0 n − 1 ( i n − 1 ) ⋅ i k ⋅ 2 n ( n − 1 ) 2 − ( n − 1 ) \sum _{i=0} ^{n - 1} {i \choose n - 1} \cdot i^k \cdot 2 ^ {\frac {n(n-1)} 2 - (n-1)} i=0n1(n1i)ik22n(n1)(n1)

即求 ∑ i = 0 n − 1 ( i n − 1 ) i k \sum _{i=0} ^{n-1} {i \choose n - 1} i^k i=0n1(n1i)ik

m n = ∑ i = 0 m S ( n , i ) ( i m ) i ! m^n = \sum _{i=0} ^m S (n, i) {i \choose m} i! mn=i=0mS(n,i)(mi)i!,其中 S ( n , i ) S(n, i) S(n,i)为第二类斯特林数

枚举 i i i个盒子非空,方案数为 S ( n , i ) S(n, i) S(n,i) ( i m ) i \choose m (mi)为选出 i i i个盒子的方案数。因为每个盒子不同,再乘 i ! i! i!

代入得: ∑ i = 0 n − 1 ( i n − 1 ) ⋅ ∑ j = 0 i S ( k , j ) ⋅ ( j i ) ⋅ j ! \sum _{i=0} ^{n - 1} {i \choose n - 1} \cdot \sum _{j=0} ^i S(k, j) \cdot {j \choose i} \cdot j! i=0n1(n1i)j=0iS(k,j)(ij)j!

j j j提到前面: ∑ j = 0 n − 1 S ( k , j ) ⋅ j ! ∑ i = j n − 1 ( i n − 1 ) ( j i ) \sum _{j = 0} ^{n - 1} S (k, j) \cdot j! \sum _{i=j} ^{n - 1} {i \choose n - 1} {j \choose i} j=0n1S(k,j)j!i=jn1(n1i)(ij)

考虑后半个式子的组合意义:从 n − 1 n-1 n1个球中挑 i i i个染黑色,再从 i i i个黑球挑 j j j个染白色。

因为枚举 i i i,所以可以取出任意个染成黑色。等价于从 n − 1 n-1 n1个球挑 j j j个染白色,剩下 n − 1 − j n-1-j n1j个球随意

∑ j = 0 n − 1 S ( k , j ) ⋅ j ! ∑ i = j n − 1 ( i n − 1 ) ( j i ) = ∑ j = 0 n − 1 S ( k , j ) ⋅ j ! ⋅ ( j n − 1 ) 2 n − 1 − j = ∑ j = 0 n − 1 S ( k , j ) ⋅ j ! ⋅ ( n − 1 ) ! j ! ( n − 1 − j ) ! ⋅ 2 n − 1 − j = ∑ j = 0 n − 1 S ( k , j ) ⋅ ( n − 1 ) ! ( n − 1 − j ) ! ⋅ 2 n − 1 − j \begin {aligned} \sum _{j = 0} ^{n - 1} S(k, j) \cdot j! \sum _{i=j} ^{n-1} {i \choose n - 1} {j \choose i} &= \sum _{j = 0} ^{n - 1} S(k, j) \cdot j! \cdot {j \choose n - 1} 2^{n - 1 - j} \\ &= \sum _{j=0} ^{n - 1} S(k, j) \cdot j! \cdot \frac {(n-1)!} {j!(n - 1 - j)!} \cdot 2^{n - 1 - j} \\ &= \sum _{j=0} ^{n-1} S(k, j) \cdot \frac {(n-1)!} {(n-1-j)!} \cdot 2^{n - 1 - j} \end {aligned} j=0n1S(k,j)j!i=jn1(n1i)(ij)=j=0n1S(k,j)j!(n1j)2n1j=j=0n1S(k,j)j!j!(n1j)!(n1)!2n1j=j=0n1S(k,j)(n1j)!(n1)!2n1j

第二类斯特林数直接NTT预处理。

j > k j \gt k j>k时, S ( k , j ) = 0 S(k, j) = 0 S(k,j)=0。因此算到 k k k即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll ;
const int maxn = 1e6 + 10, mod = 998244353, g = 3 ;
int n, k, ginv ;
ll a[maxn], b[maxn], fac[maxn], inv[maxn] ;
ll power (ll x, int y) {
    ll res = 1 ;
    while (y) {
        if (y & 1) res = res * x % mod ;
        x = x * x % mod; y >>= 1 ;
    }
    return res ;
}
void ntt (ll a[], int n, int f) {
    for (int i = 0, j = 0; i < n; i ++) {
        if (i > j) swap (a[i], a[j]) ;
        for (int t = n >> 1; (j ^= t) < t; t >>= 1) ;
    }
    for (int i = 2; i <= n; i <<= 1) {
        ll wn = power (f ? ginv : g, (mod - 1) / i) ;
        for (int j = 0; j < n; j += i) {
            ll w = 1 ;
            for (int k = 0; k < (i >> 1); k ++, w = w * wn % mod) {
                ll A = a[j + k], B = w * a[j + k + (i >> 1)] % mod ;
                a[j + k] = (A + B) % mod; a[j + k + (i >> 1)] = (A - B + mod) % mod ;
            }
        }
    }
}
int main() {
    scanf("%d%d", &n, &k) ;
    fac[0] = inv[0] = 1 ;
    for (int i = 1; i <= k; i ++) fac[i] = fac[i - 1] * i % mod ;
    inv[k] = power (fac[k], mod - 2) ;
    for (int i = k - 1; i >= 1; i --) inv[i] = inv[i + 1] * (i + 1) % mod ;
    for (int i = 0; i <= k; i ++) a[i] = 1ll * (i & 1 ? -1 : 1) * inv[i] ;
    for (int i = 0; i <= k; i ++) b[i] = power (i, k) * inv[i] % mod ;
    int M = 2 * k, N ;
    for (N = 1; N <= M; N <<= 1) ;
    ginv = power (g, mod - 2) ;
    ntt (a, N, 0); ntt (b, N, 0) ;
    for (int i = 0; i < N; i ++) a[i] = a[i] * b[i] % mod ;
    ntt (a, N, 1) ;
    ll invn = power (N, mod - 2), inv2 = power (2, mod - 2) ;
    for (int i = 0; i < N; i ++) a[i] = a[i] * invn % mod ;
    ll ans = 0, pp = 1, p = power (2, n - 1) ;
    for (int i = 0; i <= min (n - 1, k); i ++) {
        ans = (ans + a[i] * pp % mod * p % mod) % mod ;
        pp = pp * (n - 1 - i) % mod ;
        p = p * inv2 % mod ;
    }
    ans = ans * n % mod ;
    ans = ans * power (2, 1ll * (n - 1) * (n - 2) / 2 % (mod - 1)) % mod ;
    printf("%lld\n", ans) ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值