清華集訓2017 生成樹計數

2021-10-21 05:49:22 字數 3539 閱讀 7142

在乙個\(s\)個點的圖中,存在\(s-n\)條邊,使圖中形成了\(n\)個連通塊,第\(i\)個連通塊中有\(a_i\)個點。

現在我們需要再連線\(n-1\)條邊,使該圖變成一棵樹。對一種連邊方案,設原圖中第\(i\)個連通塊連出了\(d_i\)條邊,那麼這棵樹\(t\)的價值為:

\[\mathrm(t) = \left(\prod_^ ^m\right)\left(\sum_^ ^m\right) \]

你的任務是求出所有可能的生成樹的價值之和,對\(998244353\)取模。

(可能只有我沒讀出來題目說連通塊內的連邊方式不計。)

樹和每個點的度數可以聯想到\(prufer\)序列。那麼設\(c_i\)為第\(i\)個點在\(prufer\)序列**現的次數,則\(c_i=d_i-1\)。考慮對於乙個確定的序列\(c_i\),它對答案的貢獻就是

\[\frac^nc_i!}\times (\prod_^na_i^)\times (\prod_^n(c_i+1)^m)\times (\sum_^n(c_i+1)^m) \]

第一項是這個序列對應的有標號無根樹個樹;第二個是因為每個連通塊中的點可以任意分配這個連通塊的出邊;後面兩個是題面定義的價值。

那麼有乙個暴力思路就是遞推,在那之前我們把\((n-2)!\prod_^na_i\)看作常數項,只考慮剩餘的式子。設

\[f_=\sum_=m}\frac^na_i^)\times (\prod_^n(c_i+1)^m)}^nc_i!}\\ g_=\sum_=m}\frac^na_i^)\times (\prod_^n(c_i+1)^m)}^nc_i!}\times (\sum_^n(c_i+1)^m) \]

即遞推前\(n\)個點的總\(c_i\)為\(m\)的所有情況之和。轉移就有:

\[f_=\sum_^mf_\times \frac\\ g_=\sum_^m(g_+f_\times (i+1)^m)\times \frac \]

答案就是\(g_\times \frac^na_i}\)。

現在就可以有\(20\)分的好成績了。如果用\(ntt\)實現上面的轉移就可以有\(40\)分的好成績。

然後發現我這個式子並不好優化(懶得優化兩個式子)。瞟一眼題解之後發現開始那個式子可以化得好看些:

\[\begin \frac^nc_i!}\times (\prod_^na_i^)\times (\prod_^n(c_i+1)^m)\times (\sum_^n(c_i+1)^m) \\=((n-2)!\prod_^na_i)\times (\sum_^n(c_i+1)^(\prod_^n\frac})(\prod_(c_i+1)^m)) \end \]

我們還是不管前面的常數項。可以發現每個\(c_i\)對式子的貢獻就是\(\frac(c_i+1)^m}\)或者乙個序列中僅有乙個\(c_i\)貢獻為\(\frac(c_i+1)^}\),那麼這個式子就可以由若干個次數表示\(c\)的\(egf\)乘起來(實際上如果嘗試用\(egf\)推一下那個\(n^3\)遞推可以更容易發現這種性質)。乍一看一共有\(n\)個係數不同的\(n\)次多項式,似乎不可做,但是第\(i\)個多項式每一項都有\(a_i\)的若干次方,且與\(x\)次數相同,所以這些多項式都可以寫成\(f(a_ix)\)的形式。因此設

\[a(x)=\fracx^m},b(x)=\frac \]

答案就是\(\sum_^n\frac\prod_^nb(a_jx)\)。這樣有什麼好處呢?這裡補充一下這種trick。

如果式子可以寫成\(\sum_^nf(a_ix)\)的形式,並且對任意\(m\)都求出了\(\sum_^na_i^m\),那麼只要求出\(f(x)\),式子就可以變成

\[\sum_([x^m]f(x))\sum_^na_i^m \]

因此我們求出\(\frac\)和\(\prod_^nb(a_ix)\)即可。但後面這個是\(\prod\),和前面的\(\sum\)不同,這裡就要取個\(\ln\):

\[\prod_^nb(a_ix)=e^^n\ln} \]

那麼我們算一下\(\ln\)就可以像上面那樣算了。

現在唯一的問題就是怎麼對每個\(m\)求出\(\sum_^na_i^m\)。類似於自然數冪和的推導,我們寫出這個東西的\(ogf\)就有

\[g(x)=\sum_(\sum_^na_i^m)x^m=\sum_^n\sum_a_i^mx^m=\sum_^n\frac \]

這就有點像p4705玩遊戲這題的技巧,因為

\[x(\ln(1-a_ix))'=-\frac=-\frac+1 \]

那設\(h(x)=\sum_^n(\ln(1-a_ix))'\),那\(g(x)=-xh(x)+n\)。求\(h\)就:

\[h(x)=(\sum_^n\ln(1-a_ix))'=(\ln\prod_^n(1-a_ix))' \]

分治\(ntt\)即可。至此這題就解決了,複雜度瓶頸為最後分治\(ntt\)的\(\mathcal(n\log^2n)\)。

#include#define rg register

#define il inline

#define cn const

#define gc getchar()

#define fp(i, a, b) for(int i = (a), ed = (b); i <= ed; ++i)

#define fb(i, a, b) for(int i = (a), ed = (b); i >= ed; --i)

#define go(u) for(int i = head[u]; ~i; i = e[i].nxt)

using namespace std;

typedef cn int cint;

typedef long long ll;

il void rd(int &x)

while('0' <= c && c <= '9')x = (x<<1)+(x<<3)+(c^48), c = gc;

x *= f;

}cint maxn = 30010, mod = 998244353, g = 3, invg = (mod+1)/3;

int n, m, a[maxn], fac[maxn], ifac[maxn], inv[maxn], mul = 1;

int lim, hst, rev[maxn<<2], a[maxn<<2], b[maxn<<2], ln[maxn<<2], invb[maxn<<2];

int h[maxn<<2], e[maxn<<2];

il int fpow(int a, int b, int ans = 1)

il void ntt(int *a, cint &typ)

} }if(typ)

}il void init(int n)

int ln_ary[maxn<<2];

il void get_ln(int *a, int *f, int n)

int exp_ary[maxn<<2];

void get_exp(int *a, int *f, int n)

int div_ary[19][maxn<<2];

void divntt(int d, int l, int r)

int main()

清華集訓2017 生成樹計數 生成函式

bzoj5119 考慮任一長度為 n 2 的序列,序列中每個數權值為 1,n 這個序列 prufer 序列 唯一對應一棵形態確定的 n 個節點的樹,反之亦然,即樹和 prufer 序列是雙射關係。那麼可以將問題轉化為列舉 prufer 序列 beginans sum d i n 2 frac pro...

uva 10766 生成樹計數

給出n,m,k,代表一家公司有n個部門,編號1到n,有m組關係,表示i和j不能直接聯通,k代表主管部門,問你有多少種分層方案。這道題的k沒有什麼用。include include include include include include include include include incl...

uva10766生成樹計數

此類題是給定乙個無向圖,求所有生成樹的個數,生成樹計數要用到matrix tree定理 kirchhoff矩陣 樹定理 g的度數矩陣d g 是乙個n n的矩陣,並且滿足 當i j時,dij 0 當i j時,dij等於vi的度數 g的鄰接矩陣a g 也是乙個n n的矩陣,並且滿足 如果vi vj之間有...