【bzoj5093】[Lydsy1711月赛]图的价值(NTT+第二类斯特林数)

题意:
给定\(n\)个点,一个图的价值定义为所有点的度数的\(k\)次方之和。
现在计算所有\(n\)个点的简单无向图的价值之和。

思路:
将式子列出来:
\[
\sum_{i=1}^n\sum_{j=0}^{n-1}{n-1\choose j}2^{\frac{(n-1)(n-2)}{2}}j^k
\]
表示分别考虑每个点的贡献,我们只需要枚举其度数即可,其余的边任意连。
然后我们将后面的\(j^k\)用第二类斯特林数展开:
\[
\begin{aligned}
&\sum_{i=1}^{n}\sum_{j=0}^{n-1}{n-1\choose j}2^{\frac{(n-1)(n-2)}{2}}\sum_{t=1}^{j}{j\choose t}t!\begin{Bmatrix}
k \\ t
\end{Bmatrix}\ =&\sum_{i=1}^n2^{\frac{(n-1)(n-2)}{2}}\sum_{t=1}^{n-1}t!\begin{Bmatrix}
k \\ t
\end{Bmatrix}\sum_{j=t}^{n-1}{n - 1 \choose j}{j \choose t}\ =&\sum_{i=1}^n2^{\frac{(n-1)(n-2)}{2}}\sum_{t=1}^{n-1}t!\begin{Bmatrix}
k \\ t
\end{Bmatrix}\sum_{j=t}^{n-1}{n - 1 \choose j}{j \choose t}\ =&\sum_{i=1}^n2^{\frac{(n-1)(n-2)}{2}}\sum_{t=1}^{n-1}t!{n-1\choose t}\begin{Bmatrix}
k \\ t
\end{Bmatrix}\sum_{j=t}^{n-1}{n - 1 - t\choose j - t}\ =&\sum_{i=1}^n2^{\frac{(n-1)(n-2)}{2}}\cdot\sum_{t=1}^{n-1}2^{n-1-t}t!{n-1\choose t}\begin{Bmatrix}
k \\ t
\end{Bmatrix}\ =&\sum_{i=1}^n2^{\frac{(n-1)(n-2)}{2}}(n-1)!\cdot\sum_{t=1}^{n-1}\frac{2^{n-1-t}}{(n-1-t)!}\begin{Bmatrix}
k \\ t
\end{Bmatrix}
\end{aligned}
\]
似乎可以不要最后一行,对于每个点预处理之后可以直接\(O(n)\)算了。
因为卷积系数要求\(\displaystyle \begin{Bmatrix} k \\ t \end{Bmatrix}\),注意到这是一行的第二类斯特林数,那么我们可以直接通过\(FFT\)在\(O(klogk)\)的时间内预处理出来。预处理详见:传送门
代码如下:

/*
 * Author:  heyuhhh
 * Created Time:  2019/12/11 22:57:14
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5, MOD = 998244353;

int n, k, m;
ll qpow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}
const int P = 998244353, G = 3, Gi = 332748118;
int lim = 1, L, r[N * 4];
ll a[N * 4], b[N * 4];
void NTT(ll *A, int type) {
    for(int i = 0; i < lim; i++)
        if(i < r[i]) swap(A[i], A[r[i]]);
    for(int mid = 1; mid < lim; mid <<= 1) {
        ll Wn = qpow( type == 1 ? G : Gi , (P - 1) / (mid << 1)); //Wn = g ^ ((p - 1) / n)  (mod p)
        for(int j = 0; j < lim; j += (mid << 1)) {
            ll w = 1;
            for(int k = 0; k < mid; k++, w = (w * Wn) % P) {
                 int x = A[j + k], y = w * A[j + k + mid] % P;
                 A[j + k] = (x + y) % P,
                 A[j + k + mid] = (x - y + P) % P;
            }
        }
    }
}

void solve(ll *a, ll *b) {
    while(lim <= m + m) lim <<= 1, L++;
    for(int i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
    for(int i = n + 1; i < lim; i++) a[i] = 0;  //a,b need init
    for(int i = m + 1; i < lim; i++) b[i] = 0;
    NTT(a, 1); NTT(b, 1);
    for(int i = 0; i < lim; i++) a[i] = (a[i] * b[i]) % P;
    NTT(a, -1);
    ll inv = qpow(lim, P - 2);
    for(int i = 0; i < lim; i++) a[i] = a[i] * inv % P;
}

int fac[N], inv[N], c[N];

void init() {
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
    c[0] = 1;
    for(int i = 1; i < N; i++) c[i] = 1ll * c[i - 1] * (n - i) % MOD * qpow(i, MOD - 2) % MOD;
    m = k;
    for(int i = 0; i <= m; i++) {
        a[i] = (i & 1) ? MOD - inv[i] : inv[i];
        b[i] = qpow(i, k) * inv[i] % MOD;
    }
    solve(a, b);
}

void run(){
    ll ans = 1ll * n * qpow(2, 1ll * (n - 1) * (n - 2) / 2) % MOD;
    ll res = 0;
    for(int i = 0; i <= m; i++) {
        res = (res + a[i] * fac[i] % MOD * c[i] % MOD * qpow(2, n - i - 1) % MOD) % MOD;
    }
    ans = ans * res % MOD;
    cout << ans << '\n';
}

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

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

时间: 2024-08-30 12:17:11

【bzoj5093】[Lydsy1711月赛]图的价值(NTT+第二类斯特林数)的相关文章

bzoj 5093 [Lydsy1711月赛]图的价值 NTT+第二类斯特林数

[Lydsy1711月赛]图的价值 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 245  Solved: 128[Submit][Status][Discuss] Description “简单无向图”是指无重边.无自环的无向图(不一定连通). 一个带标号的图的价值定义为每个点度数的k次方的和. 给定n和k,请计算所有n个点的带标号的简单无向图的价值之和. 因为答案很大,请对998244353取模输出. Input 第一行包含两个正整数n,k(

【BZOJ5093】图的价值(第二类斯特林数,组合数学,NTT)

[BZOJ5093]图的价值(第二类斯特林数,组合数学,NTT) 题面 BZOJ 题解 单独考虑每一个点的贡献: 因为不知道它连了几条边,所以枚举一下 \[\sum_{i=0}^{n-1}C_{n-1}^i·i^k·2^{\frac{n(n-1)}{2}}\] 因为有\(n\)个点,所以还要乘以一个\(n\) 所以,我们真正要求的就是: \[\sum_{i=0}^{n-1}C_{n-1}^i·i^k\] 怎么做? 看到了\(i^k\)想到了第二类斯特林数 \[m^n=\sum_{i=0}^{m}

bzoj5093:图的价值(第二类斯特林数+NTT)

传送门 首先,题目所求为\[n\times 2^{C_{n-1}^2}\sum_{i=0}^{n-1}C_{n-1}^ii^k\] 即对于每个点\(i\),枚举它的度数,然后计算方案.因为有\(n\)个点,且关于某个点连边的时候剩下的边都可以随便连,所以有前面的两个常数 所以真正要计算的是\[\sum_{i=0}^{n-1}C_{n-1}^ii^k\] 根据第二类斯特林数的性质,有\[i^k=\sum_{j=0}^iS(k,j)\times j!\times C_i^j\] 然后带入,得\[\s

【BZOJ 4555】[Tjoi2016&amp;Heoi2016]求和 NTT+第二类斯特林数

用到第二类斯特林数的性质,做法好像很多,我打的是直接ntt,由第二类斯特林数的容斥公式可以推出,我们可以对于每一个i,来一次ntt求出他与所有j组成的第二类斯特林数的值,这个时候我们是O(n^2logn)的,还不如暴力,但是我们发现,对于刚刚提到的容斥的式子,将其化为卷积形式后,其一边的每一项对于每一个i都相同,另一边的每一项是对于所有的i形成一个n项的等比数列,这样我们可以把成等比数列的一边求和,用固定的一边去卷他们的和,这时候的答案的每一项就是所有的i的这一项的和,然后我们再O(n)乘上阶乘

bzoj 4555 [Tjoi2016&amp;Heoi2016]求和 NTT 第二类斯特林数 等比数列求和优化

[Tjoi2016&Heoi2016]求和 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 679  Solved: 534[Submit][Status][Discuss] Description 在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心. 现在他想计算这样一个函数的值: S(i, j)表示第二类斯特林数,递推公式为: S(i, j) = j ∗ S(i − 1, j) + S(i − 1, j − 1), 1 <= j &l

bzoj 5093 图的价值 —— 第二类斯特林数+NTT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5093 每个点都是等价的,从点的贡献来看,得到式子: \( ans = n * \sum\limits_{d=0}^{n-1} d^{k} * 2^{C_{n-1}^{2}} * C_{n-1}^{d} \) 使用 \( n^{k} = \sum\limits_{i=0}^{k} S(k,i) * i! *C_{n}^{i} \) 得到 \( ans = n * \sum\limits_{d

【BZOJ4555】求和(第二类斯特林数,组合数学,NTT)

[BZOJ4555]求和(第二类斯特林数,组合数学,NTT) 题面 BZOJ 题解 推推柿子 \[\sum_{i=0}^n\sum_{j=0}^iS(i,j)·j!·2^j\] \[=\sum_{i=0}^n\sum_{j=0}^nS(i,j)·j!·2^j\] \[=\sum_{i=0}^n\sum_{j=0}^nj!·2^j(\frac{1}{j!}\sum_{k=0}^j(-1)^k·C_j^k·(j-k)^i)\] \[=\sum_{j=0}^n2^j\sum_{k=0}^j(-1)^k

BZOJ4555 [Tjoi2016&amp;Heoi2016]求和 【第二类斯特林数 + NTT】

题目 在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心. 现在他想计算这样一个函数的值: S(i, j)表示第二类斯特林数,递推公式为: S(i, j) = j ? S(i ? 1, j) + S(i ? 1, j ? 1), 1 <= j <= i ? 1. 边界条件为:S(i, i) = 1(0 <= i), S(i, 0) = 0(1 <= i) 你能帮帮他吗? 输入格式 输入只有一个正整数 输出格式 输出f(n).由于结果会很大,输出f(n)对998244353(7

第二类斯特林数总结

最主要的两个式子: 套路1: $$\begin{array}{l}x^k=\sum_{i=0}^x\begin{pmatrix}x\\i\end{pmatrix}\begin{Bmatrix}k\\i\end{Bmatrix}i!\\\end{array}$$ 左边的式子可以看成将k个球放到x个有标号的盒子中,总共的方案数. 右边的式子可以看成先选出i个非空的盒子,将k个球放到这i个盒子中且不空的方案数.但由于第二类斯特林数是无序的,还要乘上i阶乘. 套路2: $$\begin{array}{l