ZJOI2020 抽卡(拉格朗日反演)

题目
二元生成函数与拉格朗日反演:
比较好的形式:对于二元 G F GF GF,第二个元一般来说都是来辅助的,因为一个元用来拉格朗日反演了,另一个元就可以让我们来做多项式操作。
比如二元 G F : GF: GF: H ( u , z ) = 1 1 − u F ( z ) H(u,z) = \frac {1}{1 - uF(z)} H(u,z)=1uF(z)1 F ( z ) F(z) F(z)有复合逆 G ( z ) G(z) G(z)
则由于拓展拉格朗日反演: [ z n ] 1 1 − u F ( z ) = 1 n [ z n − 1 ] d 1 1 − u z d z ( z G ( z ) ) n [z^n]\frac 1{1-uF(z)} = \frac 1n[z^{n-1}]\frac {d\frac 1{1-uz}}{dz}(\frac {z}{G(z)})^n [zn]1uF(z)1=n1[zn1]dzd1uz1(G(z)z)n
因为 d 1 1 − u z d z = ( ∑ i = 0 u i z i ) ′ = ∑ i = 0 u i + 1 ( i + 1 ) z i \frac {d\frac 1{1-uz}}{dz} = (\sum_{i=0} u^iz^i) ' = \sum_{i=0} u^{i+1}(i+1)z^i dzd1uz1=(i=0uizi)=i=0ui+1(i+1)zi
所以我们可以在得到 ( z G ( z ) ) n (\frac {z}{G(z)})^n (G(z)z)n O ( n ) O(n) O(n)求出 H ( u , z ) [ z n ] = ∑ i = 0 n [ z n ] F ( z ) i u i H(u,z)[z^n] = \sum_{i=0}^n [z^n]F(z)^iu^i H(u,z)[zn]=i=0n[zn]F(z)iui
也就是说我们可以得到 ∀ i ∈ [ 0 , n ] [ z n ] F ( z ) i \forall i\in[0,n] [z^n]F(z)^i i[0,n][zn]F(z)i
对于 i > n i \gt n i>n的情况因为 [ z 0 ] F ( z ) = 0 [z^0]F(z) = 0 [z0]F(z)=0,所以 [ z n ] F ( z ) i = 0 [z^n]F(z)^i = 0 [zn]F(z)i=0
对于没有复合逆的情况也就是:
一: [ z 0 ] F ( z ) = c [z^0]F(z) = c [z0]F(z)=c,则求出 P ( z ) = F ( z ) − c P(z) = F(z) - c P(z)=F(z)c的之后再二项式展开求出 F ( z ) F(z) F(z)即可。
二: F ( z ) F(z) F(z)的最低次为 k ≥ 2 k \geq 2 k2,则将 F ( z ) F(z) F(z) k k k次根得到 P ( z ) P(z) P(z)后,求出 [ z n ] P ( z ) i [z^n]P(z)^i [zn]P(z)i,然后再代换回去即可。

原题可以转化为求长度为 n n n的序列中求选出 x = 0 , 1 , 2... n x=0,1,2...n x=0,1,2...n个位置,不存在连续的 k k k个位置被选的方案数。
可以转化为从左到右每次选择 0.... k − 1 0....k-1 0....k1个位置被选,然后下一个位置强制不选(于是乎我们需要在最后在加一个位置。)
那么这个生成函数就是 F ( z ) = ∑ i = 0 k − 1 z i + 1 = z 1 − z k 1 − z F(z) =\sum_{i=0}^{k-1} z^{i+1} = z\frac {1-z^k}{1-z} F(z)=i=0k1zi+1=z1z1zk
选出了 n + 1 − x n+1-x n+1x个位置,也就是进行了上述的 x x x次操作,所以我们相当于要求
[ z n + 1 ] F ( z ) i , ∀ i ∈ [ 0 , n + 1 ] [z^{n+1}]F(z)^i , \forall i \in[0,n+1] [zn+1]F(z)i,i[0,n+1]。、
如果有 F ( z ) F(z) F(z)的复合逆我们就可以套用上述做法。
考虑 F ( z ) F(z) F(z)的复合逆 G ( z ) G(z) G(z)满足
G ( z ) k + 1 − G ( z ) = z ( G ( z ) − 1 ) G(z)^{k+1}-G(z) = z(G(z)-1) G(z)k+1G(z)=z(G(z)1)
也就是 G ( z ) k + 1 − ( z + 1 ) G ( z ) + z = 0 G(z)^{k+1}-(z+1)G(z)+z = 0 G(z)k+1(z+1)G(z)+z=0
牛顿迭代:
H ( x ) = x k + 1 − ( z + 1 ) x + z = 0 H(x) = x^{k+1} - (z+1)x+z = 0 H(x)=xk+1(z+1)x+z=0
x ≡ x 0 − x 0 k + 1 − ( z + 1 ) x 0 + z ( k + 1 ) x 0 k − z − 1 x \equiv x_0 - \frac {x_0^{k+1}-(z+1)x_0+z}{(k+1)x_0^k-z-1} xx0(k+1)x0kz1x0k+1(z+1)x0+z(感谢友善的出题人让分母可以直接求逆。)
所以这个题整个就是个多项式快速幂。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 600005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 998244353
#define LL long long
using namespace std;

int Wl=1,Wl2,w[maxn],lg[maxn],inv[maxn],fac[maxn],invf[maxn];
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
void init(int n){
	for(;n>=Wl<<1;Wl<<=1);
	int pw = Pow(3,(mod-1)/(Wl2=Wl<<1));
	w[Wl] = inv[0] = inv[1] = fac[0] = fac[1] = invf[0] = invf[1] = 1;
	rep(i,Wl+1,Wl2) w[i] = 1ll * w[i-1] * pw % mod;
	per(i,Wl-1,1) w[i] = w[i<<1];
	rep(i,2,Wl2) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod , fac[i] = 1ll * fac[i-1] * i % mod,
		invf[i] = 1ll * invf[i-1] * inv[i] % mod , lg[i] = lg[i>>1] + 1;
}
int upd(int x){ return x += x >> 31 & mod; }
void NTT(int *A,int n,int tp){
	static int r[maxn]={};
	static unsigned long long ar[maxn];
	if(tp ^ 1) reverse(A+1,A+n);
	rep(i,0,n-1) r[i] = r[i>>1]>>1|(i&1)<<lg[n]-1,ar[i] = upd(A[r[i]]);
	for(int L=1;L<n;L<<=1) for(int s=0,L2=L<<1;s<n;s+=L2) for(int k=s,x=L,t;k<s+L;k++,x++)
		t = ar[k+L] * w[x] % mod , ar[k+L] = ar[k] - t + mod , ar[k] += t;
	rep(i,0,n-1) A[i] = ar[i] % mod;
	if(tp ^ 1) rep(i,0,n-1) A[i] = 1ll * A[i] * inv[n] % mod;
}
void Inv(int *A,int *B,int n){
	static int t[maxn];
	B[B[1] = 0] = Pow(A[0] , mod-2);
	for(int k=2,L=4;k<(n<<1);k<<=1,L<<=1){
		rep(i,0,L-1) t[i] = i < k ? A[i] : B[i] = 0;
		NTT(t,L,1) , NTT(B,L,1);
		rep(i,0,L-1) B[i] = B[i] * (2 - 1ll * t[i] * B[i] % mod) % mod;
		NTT(B,L,-1);
		rep(i,min(k,n),L-1) B[i] = 0;
	}
}
void Ln(int *A,int *B,int n){
	static int t[maxn];
	int L = 1 << lg[2 * n - 2] + 1;
	Inv(A,B,n);
	rep(i,0,L-1) t[i] = i < n - 1 ? 1ll * (i + 1) * A[i+1] % mod : B[i] = 0;
	NTT(t,L,1) , NTT(B,L,1);
	rep(i,0,L-1) t[i] = 1ll * t[i] * B[i] % mod;
	NTT(t,L,-1);
	B[0] = 0;
	rep(i,1,L-1) B[i] = i < n ? 1ll * t[i-1] * inv[i] % mod : 0; 
}
void Exp(int *A,int *B,int n){
	static int t[maxn];
	B[B[1] = 0] = 1;
	for(int k=2,L=4;k<(n<<1);k<<=1,L<<=1){
		Ln(B,t,k);
		rep(i,0,L-1) t[i] = i < k ? ((i == 0) - t[i] + A[i]) % mod : B[i] = 0; 
		NTT(t,L,1) , NTT(B,L,1);
		rep(i,0,L-1) B[i] = 1ll * B[i] * t[i] % mod;
		NTT(B,L,-1);
		rep(i,min(k,n),L-1) B[i] = 0;
	}
}
void Pow(int *A,int *B,int n,int K){
	LL x = 0;
	for(;x < n && A[x] == 0;x ++);
	static int t[maxn];
	int L = 1 << lg[n-1] + 1;
	rep(i,0,L-1) t[i] = i+x < n ? A[i+x] : 0;
	x *= K; 
	if(x >= n){
		rep(i,0,L-1) B[i] = 0;
		return;
	}
	Ln(t,B,L);
	rep(i,0,L-1) B[i] = 1ll * K * B[i] % mod;
	Exp(B,t,L);
	rep(i,0,L-1) B[i] = (i < n && i-x >= 0) ? t[i-x] : 0;
}
void Mul(int *A,int *B,int *C,int n,int m,int cut = 0x3f3f3f3f){
	static int st[2][maxn];
	int L = 1 << lg[n+m-2] + 1;
	rep(i,0,L-1) st[0][i] = i < n ? A[i] : 0 , st[1][i]  =i < m ? B[i] : 0;
	NTT(st[0],L,1) , NTT(st[1],L,1);
	rep(i,0,L-1) C[i] = 1ll * st[0][i] * st[1][i] % mod;
	NTT(C,L,-1);
	rep(i,cut,L-1) C[i] = 0;
}
void Solve(int *B,int n,int K){
	static int t[3][maxn];
	B[0] = B[1] = 0;
	for(int k=2,L=4;k<(n<<1);k<<=1,L<<=1){
		Pow(B,t[1],k,K);
		Mul(t[1],B,t[0],k,k,k);
		rep(i,0,k-1) t[0][i] = (1ll * t[0][i] - B[i] - (i?B[i-1]:0)) % mod;
		t[0][1]++;
		rep(i,0,k-1) t[1][i] = (K+1ll) * t[1][i] % mod;
		t[1][0] -- , t[1][1] --;
		Inv(t[1],t[2],k);
		Mul(t[0],t[2],t[1],k,k,k);
		rep(i,0,k-1) B[i] = (B[i] - t[1][i]) % mod;
		rep(i,min(k,n),L-1) B[i] = 0;
	}
}
#define poly vector
#define Ct const
poly Mul(Ct poly &A,Ct poly &B){
	static int st[2][maxn];
	int L = 1 << lg[A.size() + B.size() - 2] + 1;
	rep(i,0,L-1) st[0][i] = i < A.size() ? A[i] : 0 , st[1][i]  =i < B.size() ? B[i] : 0;
	NTT(st[0],L,1) , NTT(st[1],L,1);
	rep(i,0,L-1) st[0][i] = 1ll * st[0][i] * st[1][i] % mod;
	NTT(st[0],L,-1);
	poly C;C.resize(A.size() + B.size() - 1);
	rep(i,0,A.size()+B.size()-2) C[i] = st[0][i];
	return C;
}

int m,K,n;
int G[maxn] , cnt[maxn] , vis[maxn] , A[maxn] , B[maxn];

struct cmp{
	bool operator ()(const poly &u,const poly &v){
		return u.size() > v.size();
	}
};
priority_queue<poly,vector<poly>,cmp>q;

int main(){
	scanf("%d%d",&m,&K);
	rep(i,1,m){
		int x;
		scanf("%d",&x);
		vis[x] = 1;
		n = max(n , x);
	}
	init(2 * (m + 2));
	Solve(G,m+2,K);
	for(int i=1,j;i<=n;i=j){
		for(j = i;j <=n && vis[i] == vis[j];j++);
		if(vis[i]) cnt[j - i] ++;
	}
	rep(i,0,m) G[i] = G[i+1];
 	rep(i,1,m) if(cnt[i]){
		Pow(G,A,i+1,mod-i-1);
		poly C(i+1,0);
		rep(j,0,i) C[i-j] = (j+1ll) * A[i-j] % mod * inv[i+1] % mod; 
		rep(j,1,cnt[i]) q.push(C);
	}
	
	for(;q.size() >= 2;){
		poly u = q.top() ; q.pop();
		poly v = q.top() ; q.pop();
		q.push(Mul(v,u));
	}
	poly ans = q.top();
	
	int ret = 0;
	rep(i,0,min((int)ans.size()-1,m)) 
		ret = (ret + 1ll * m * inv[m - i] % mod * invf[m] % mod * fac[i] % mod * fac[m - i] % mod * ans[i]) % mod;
	printf("%d\n",(ret+mod)%mod);
}

P S : PS: PS:这个做法被直接分治 F F T FFT FFT时间效率上吊打。
我今天又模拟赛又做到一个类似这个做法的题,标算是拉格朗日反演但是被分治FFT吊打,估计得等超快exp普及了之后才能反超。

你可能感兴趣的:(生成函数)