卧薪尝胆,厚积薄发。
清华集训2017 生成树计数
Date: Tue Mar 12 00:31:17 CST 2019
In Category:
NoCategory
Description:
在一个
$s$
个点的图
$s-n$
条边使图中形成了
$n$
个连通块,第
$i$
个连通块中有
$a_i$
个点。再连
$n-1$
条边,使该图变成一棵树。对一种连边方案,设原图中第
$i$
个连通块连出了
$d_i$
条边,那么这棵树
$T$
的价值为:
$$
\mathrm{val}(T) = \left(\prod_{i=1}^{n} {d_i}^m\right)\left(\sum_{i=1}^{n} {d_i}^m\right)
$$
求出所有可能的生成树的价值之和,对
$998244353$
取模。
$1\leqslant n\leqslant 30000$
Solution:
直接不考虑连通块,把原问题转换成
$n$
个点连成一棵生成树,那么式子也就相应要变成:
$$
val(T)=\prod_{i=1}^na_i^{d_i}d_i^m\sum_{i=1}^nd_i^m
$$
考虑到度数和
$prufer$
序列的关系,我们可以思考能不能用
$prufer$
序列来解决这个问题,因为枚举
$prufer$
序列相当于枚举树,我们可以列出如下式子:
$$
\begin{align}
ans&=\sum_{d_1+d_2+\cdots d_k=n-2}\binom{n-2}{d_1,d_2,\dots,d_k}\prod_{i=1}^na_i^{d_i+1}(d_i+1)^m\sum_{i=1}^n(d_i+1)^m\\
&=(n-2)!\prod_{i=1}^na_i\sum_{d_1+d_2+\cdots d_n=n-2}\prod_{i=1}^n\frac{a_i^{d_i}}{d_i!}(d_i+1)^m\sum_{i=1}^n(d_i+1)^m
\end{align}
$$
前面是一个常量不用管:
$$
\begin{align}
&\sum_{d_1+d_2+\cdots d_n=n-2}\bigg(\prod_{i=1}^n\frac{a_i^{d_i}}{d_i!}(d_i+1)^m\bigg)\bigg(\sum_{i=1}^n(d_i+1)^m\bigg)\\
=&\sum_{d_1+d_2+\cdots d_n=n-2}\bigg(\sum_{i=1}^n\frac{a_i^{d_i}}{d_i!}(d_i+1)^{2m}\prod_{j=1,i\ne j}^n\frac{a_i^{d_i}}{d_i!}(d_j+1)^m\bigg)\\
\end{align}
$$
构造两个生成函数:
$$
\begin{align}
A(x)&=\sum_{i\geqslant 0}\frac{(i+1)^{2m}}{i!}x^i\\
B(x)&=\sum_{i\geqslant 0}\frac{(i+1)^m}{i!}x^i
\end{align}
$$
那么:
$$
ans=[x^{n-2}]\sum_{i=1}^nA(a_ix)\prod_{j=1,j\ne i}^nB(a_jx)
$$
这一步转化真是妙啊,思考一下指数会发现刚好成立。
那么:
$$
ans=[x^{n-2}]\sum_{i=1}^n\frac{A(a_ix)}{B(a_ix)}\prod_{j=1}^nB(a_jx)=[x^{n-2}]\bigg(\sum_{i=1}^n\frac{A(a_ix)}{B(a_ix)}\bigg)\times\bigg(\exp\sum_{i=1}^n\ln B(a_ix)\bigg)
$$
先考虑这个怎么做,可以把
$\frac{A(x)}{B(x)}$
和
$\ln B(x)$
求出来后把
$a_ix$
代入再求和,把式子展开一下就会发现其实是要求一个:
$$
\forall j\in[1,n]计算s_i=\sum_{j=1}^na_j^i
$$
这个直接套用
luogu4705玩游戏
那题的做法就行了。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 120010
int a[MAXN];
int rev[MAXN];
int ww[MAXN << 1],*g = ww + MAXN;
#define P 998244353
int power(int a,int b)
{
int res = 1;
for(;b > 0;b = b >> 1,a = 1ll * a * a % P)if(b & 1)res = 1ll * res * a % P;
return res;
}
int inver(int a){return power(a,P - 2);}
int init(int n)
{
int l = 0,len = 1;
for(;len <= n;len = len << 1)++l;
for(int i = 0;i < len;++i)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
g[0] = g[-len] = 1;g[1] = g[1 - len] = power(3,(P - 1) / len);
for(int i = 2;i < len;++i)g[i] = g[i - len] = 1ll * g[i - 1] * g[1] % P;
return len;
}
void NTT(int f[],int l,int type)
{
for(int i = 0;i < l;++i)
{
if(i < rev[i])swap(f[i],f[rev[i]]);
}
for(int i = 2;i <= l;i = i << 1)
{
int wn = g[type * l / i];
for(int j = 0;j < l;j += i)
{
int w = 1;
for(int k = j;k < j + i / 2;++k)
{
int u = f[k],t = 1ll * w * f[k + i / 2] % P;
f[k] = (u + t) % P;
f[k + i / 2] = (u - t + P) % P;
w = 1ll * w * wn % P;
}
}
}
if(type == -1)
{
int ni = power(l,P - 2);
for(int i = 0;i < l;++i)f[i] = 1ll * f[i] * ni % P;
}
return;
}
int invt[MAXN];
void poly_inv(int deg,int a[],int b[])
{
if(deg == 1)
{
b[0] = power(a[0],P - 2);
return;
}
poly_inv((deg + 1) >> 1,a,b);
int l = init(deg * 2);
for(int i = 0;i < deg;++i)invt[i] = a[i];
for(int i = deg;i < l;++i)invt[i] = 0;
NTT(invt,l,1);NTT(b,l,1);
for(int i = 0;i < l;++i)b[i] = (2ll * b[i] - 1ll * invt[i] * b[i] % P * b[i] % P + P) % P;
NTT(b,l,-1);
for(int i = deg;i < l;++i)b[i] = 0;
for(int i = 0;i < l;++i)invt[i] = 0;
return;
}
int lnt[MAXN];
void poly_ln(int deg,int a[],int b[])
{
poly_inv(deg,a,lnt);
for(int i = 0;i < deg;++i)b[i] = a[i];
for(int i = 0;i < deg;++i)b[i] = 1ll * (i + 1) * b[i + 1] % P;
int l = init(deg * 2);
NTT(lnt,l,1);NTT(b,l,1);
for(int i = 0;i < l;++i)b[i] = 1ll * b[i] * lnt[i] % P;
NTT(b,l,-1);
for(int i = deg - 1;i >= 1;--i)b[i] = 1ll * inver(i) * b[i - 1] % P;b[0] = 0;
for(int i = deg;i < l;++i)b[i] = 0;
for(int i = 0;i < l;++i)lnt[i] = 0;
return;
}
int expt[MAXN];
void poly_exp(int deg,int a[],int b[])
{
if(deg == 1)
{
b[0] = 1;
return;
}
poly_exp((deg + 1) >> 1,a,b);
poly_ln(deg,b,expt);
for(int i = 0;i < deg;++i)expt[i] = (a[i] - expt[i] + P) % P;
expt[0] = (expt[0] + 1) % P;
int l = init(deg * 2);
NTT(expt,l,1);NTT(b,l,1);
for(int i = 0;i < l;++i)b[i] = 1ll * b[i] * expt[i] % P;
NTT(b,l,-1);
for(int i = deg;i < l;++i)b[i] = 0;
for(int i = 0;i < l;++i)expt[i] = 0;
return;
}
int s[19][MAXN];
void solve(int d,int l,int r)
{
if(l == r)
{
s[d][0] = 1;s[d][1] = (P - a[l]) % P;
return;
}
int mid = ((l + r) >> 1);
solve(d + 1,l,mid);
for(int i = 0;i <= r - l + 1;++i)s[d][i] = s[d + 1][i];
for(int i = 0;i <= r - l + 1;++i)s[d + 1][i] = 0;
solve(d + 1,mid + 1,r);
int len = init(r - l + 1);
NTT(s[d],len,1);NTT(s[d + 1],len,1);
for(int i = 0;i < len;++i)s[d][i] = 1ll * s[d][i] * s[d + 1][i] % P;
NTT(s[d],len,-1);
for(int i = 0;i < len;++i)s[d + 1][i] = 0;
return;
}
int S[MAXN];
int fac[MAXN],inv[MAXN];
int A[MAXN],B[MAXN];
int C[MAXN],D[MAXN],E[MAXN];
int main()
{
scanf("%d%d",&n,&m);
int mul = 1;
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
for(int i = 1;i <= n;++i)mul = 1ll * mul * a[i] % P;
solve(0,1,n);
poly_ln(n + 1,s[0],S);
for(int i = 0;i <= n;++i)S[i] = 1ll * (i + 1) * S[i + 1] % P;
for(int i = n;i >= 1;--i)S[i] = (P - S[i - 1]) % P;S[0] = n;
fac[0] = 1;for(int i = 1;i <= n;++i)fac[i] = 1ll * fac[i - 1] * i % P;
inv[n] = inver(fac[n]);for(int i = n - 1;i >= 0;--i)inv[i] = 1ll * inv[i + 1] * (i + 1) % P;
for(int i = 0;i <= n;++i)A[i] = 1ll * power(i + 1,2 * m) * inv[i] % P;
for(int i = 0;i <= n;++i)B[i] = 1ll * power(i + 1,m) * inv[i] % P;
poly_inv(n + 1,B,C);
int l = init(n * 2);
NTT(C,l,1);NTT(A,l,1);
for(int i = 0;i < l;++i)C[i] = 1ll * C[i] * A[i] % P;
NTT(C,l,-1);
for(int i = n + 1;i < l;++i)C[i] = 0;
poly_ln(n + 1,B,D);
for(int i = 0;i <= n;++i)C[i] = 1ll * C[i] * S[i] % P;
for(int i = 0;i <= n;++i)D[i] = 1ll * D[i] * S[i] % P;
poly_exp(n + 1,D,E);
l = init(n * 2);
NTT(C,l,1);NTT(E,l,1);
for(int i = 0;i < l;++i)E[i] = 1ll * C[i] * E[i] % P;
NTT(E,l,-1);
cout << 1ll * E[n - 2] * fac[n - 2] % P * mul % P << endl;
return 0;
}
In tag:
树-prufer序列 数学-组合数学-生成函数
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡