1. 程式人生 > >【生成函式+多項式求逆】LGP5162 WD與積木

【生成函式+多項式求逆】LGP5162 WD與積木

【題目】
原題地址
n n 塊積木,給每塊積木隨機一個大小並標號,然後將相同大小的積木放在一層,再從大到小堆起來。我們只關心積木的相對大小,因此所有堆法等概率出現,求期望層數。 T , n

1 0 5 T,n\leq 10^5

【解題思路】
從期望的定義入手,我們先考慮一個樸素的 DP \text{DP}

:設 f i f_i 表示有 i i 塊積木時產生層數和, g
i g_i
表示 i i 塊積木不同堆法的數量,答案就是 f n g n \frac {f_n} {g_n} ,其中 f 0 = 0 , g 0 = 1 f_0=0,g_0=1 。我們列舉第一層放幾塊積木可以得到轉移:
g n = i = 1 n ( n i ) g n i g_n=\sum_{i=1}^n {n\choose i} g_{n-i}
f n = i = 1 n ( n i ) ( f n i + g n i ) = g n + i = 1 n ( n i ) f n i f_n=\sum_{i=1}^n {n\choose i} (f_{n-i}+g_{n-i})=g_n+\sum_{i=1}^n {n\choose i} f_{n-i}
這樣做是 O ( n 2 ) O(n^2) 的,這裡好像還有一個標號順序的問題我們好像沒有考慮,但由於方案和層數是一一對應的,所以相當於同時約掉了。

我們考慮一個更為正確的寫法:不妨設
F ( x ) = i f i i ! x i , G ( x ) = i g i i ! x i , H ( x ) = i x i i ! F(x)=\sum_{i} \frac {f_i} {i!} x^i,G(x)=\sum_{i} \frac {g_i} {i!} x^i,H(x)=\sum_{i} \frac {x^i} {i!}
由於沒有組合數的計算,我們這裡寫出的生成函式要考慮順序(實際上和前幾天的生成函式題類似,答案的形式都是 n ! k 1 ! k 2 ! \frac {n!} {k_1!k_2!\dots}

我們可以得到
2 F ( x ) = G ( x ) + F ( x ) H ( x ) 1 , 2 G ( x ) = G ( x ) H ( x ) + 1 2F(x)=G(x)+F(x)H(x)-1,2G(x)=G(x)H(x)+1
F ( x ) = G ( x ) ( G ( x ) 1 ) , G ( x ) = 1 2 H ( x ) F(x)=G(x)(G(x)-1),G(x)=\frac 1 {2-H(x)}

於是多項式求逆可以做到 O ( n log n ) O(n\log n)

【參考程式碼】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e5+5,M=264233;
const int mod=998244353,G=3;
int fac[M],ifac[M];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}
int qpow(int x,int y)
{
	int res=1;
	for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) res=(ll)res*x%mod;
	return res;
}
int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
void up(int &x,int y){x=upm(x+y);}

namespace NTT
{
	int m,L;
	int f[M],g[M],h[M],t[M],t2[M];
	int rev[M];
	void reget(int n)
	{
		for(L=0,m=1;m<2*n;++L,m<<=1);
		for(int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
	}
	void ntt(int *a,int n,int op)