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

2022-05-07 08:15:12 字數 4368 閱讀 9901

bzoj5119

考慮任一長度為$n-2$的序列,序列中每個數權值為$[1,n]$,這個序列($prufer$序列)唯一對應一棵形態確定的$n$個節點的樹,反之亦然,即樹和$prufer$序列是雙射關係。

那麼可以將問題轉化為列舉$prufer$序列:$$\beginans&=\sum_d_i=n-2}\frac(\prod_ia_i^(d_i+1)^m)*(\sum_i(d_i+1)^m)\\&=(n-2)!*(\prod_ia_i)*(\sum_d_i=n-2}(\prod_i\frac(d_i+1)^m})*(\sum_i(d_i+1)^m))\\&=(n-2)!*(\prod_ia_i)*(\sum_d_i=n-2}\sum_i((d_i+1)^m*\prod_j\frac(d_j+1)^m}))\\&=(n-2)!*(\prod_ia_i)*(\sum_d_i=n-2}\sum_i(\frac(d_i+1)^}*\prod_\frac(d_j+1)^m}))\\\end$$

設$$a(x)=\sum_^\frac}x^i\\ b(x)=\sum_^\frac}x^i\\ f(x)=\sum_ia(a_ix)\prod_b(a_jx)$$

對$f(x)$化簡:$$\beginf(x)&=\sum_ia(a_ix)\prod_b(a_jx)\\&=(\sum_i\frac)*\prod_ib(a_ix)\\&=(\sum_i\frac)*\exp(\ln(\prod_ib(a_ix)))\\&=(\sum_i\frac)*\exp(\sum_i\ln(b(a_ix)))\end$$

再設$$c(x)=\frac\\ d(x)=\ln(b(x))$$

有:$$[x^j](\sum_i\ln(b(a_ix))) = ([x^j]d(x))*\sum_ia_i^j\\ ([x^j]\sum_\frac)=([x^j]c(x))*\sum_ia_i^j$$

求出$c(x)$與$d(x)$,對它們的第$i$項乘以$\sum_ja_j^i$,也就是需要求數列的$i$次方和,我在生成函式小結裡有寫,這裡就不展開說了。

最終答案:$$ans=(n-2)!*(\prod_ia_i)*[x^]f(x)$$

$o(n\log^2 n)$

#include#include

#include

#include

#include

#define ls (x << 1)

#define rs ((x << 1) | 1)

using

namespace

std;

typedef

long

long

ll;const

int maxn = 60005, mod = 998244353, g = 3

;int add(int x, int

y)int rdc(int x, int

y)ll qpow(ll x,

inty)

return

ret;

}int n, m, lim, bit, rev[maxn<<1

], a[maxn];

ll ginv, fac[maxn], fnv[maxn], inv[maxn];

ll a[maxn

<<1], b[maxn<<1], c[maxn<<1], d[maxn<<1], ln[maxn<<1], iv[maxn<<1], f[maxn<<1], h[maxn<<1

];vector

g[maxn<<1

];void

init()

}void ntt_init(int

x)

for(int i = 1; i < lim; ++i)

rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (bit - 1

));}

void ntt(ll *x, int

y) }

}if(y == -1

)

}void get_inv(ll *x, ll *y, int

len)

get_inv(x, y, (len + 1) >> 1

);

for(int i = 0; i < len; ++i)

c[i] =y[i];

ntt_init(len

<< 1

); ntt(x, 1);

ntt(c, 1);

for(int i = 0; i < lim; ++i)

ntt(x, -1

);

for(int i = len; i < lim; ++i)

x[i] = 0;}

void get_ln(ll *x, ll *y, int

len)

ntt(x, -1

);

for(int i = len - 1; i >= 1; --i)

x[i] = x[i-1] * inv[i] %mod;

x[0] = 0

;

for(int i = len; i < lim; ++i)

x[i] = 0;}

void get_exp(ll *x, ll *y, int

len)

get_exp(x, y, (len + 1) >> 1

); get_ln(ln, x, len);

for(int i = 0; i < len; ++i)

ntt_init(len

<< 1

); ntt(x, 1);

ntt(c, 1);

for(int i = 0; i < lim; ++i)

ntt(x, -1

);

for(int i = len; i < lim; ++i)

x[i] = 0;}

void solve(int x, int l, int r, int *y)

int mid = (l + r) >> 1

; solve(ls, l, mid, y);

solve(rs, mid + 1

, r, y);

for(int i = 0; i <= mid - l + 1; ++i)

c[i] =g[ls][i];

for(int i = 0; i <= r - mid; ++i)

d[i] =g[rs][i];

ntt_init(r - l + 1

); ntt(c, 1);

ntt(d, 1);

for(int i = 0; i < lim; ++i)

ntt(c, -1

);

for(int i = 0; i <= r - l + 1; ++i)

for(int i = r - l + 2; i < lim; ++i)

c[i] = 0;}

intmain()

solve(

1, 1

, n, a);

for(int i = 0; i <= n; ++i)

d[i] = g[1

][i];

get_ln(f, d, n + 1

);

for(int i = n; i >= 1; --i)

f[0] =n;

ll tmp;

for(int i = 0; i <= n; ++i)

get_ln(d, b, n + 1

); get_inv(h, b, n + 1

); ntt_init(n

<< 1

); ntt(a, 1);

ntt(h, 1);

for(int i = 0; i < lim; ++i)

a[i] = a[i] * h[i] %mod;

ntt(a, -1

);

for(int i = 0; i <= n; ++i)

for(int i = n + 1; i < lim; ++i)

a[i] = 0

; memset(b,

0, sizeof

(b));

get_exp(b, d, n + 1

); ntt_init(n

<< 1

); ntt(a, 1);

ntt(b, 1);

for(int i = 0; i < lim; ++i)

a[i] = a[i] * b[i] %mod;

ntt(a, -1

); printf(

"%lld

", ans * a[n-2] %mod);

return0;

}

view code

清華集訓2017 生成樹計數

在乙個 s 個點的圖中,存在 s n 條邊,使圖中形成了 n 個連通塊,第 i 個連通塊中有 a i 個點。現在我們需要再連線 n 1 條邊,使該圖變成一棵樹。對一種連邊方案,設原圖中第 i 個連通塊連出了 d i 條邊,那麼這棵樹 t 的價值為 mathrm t left prod m right...

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之間有...