首页 > 技术文章 > 牛客多校第4场 G.Product EGF解决排列问题

hznumqf 2021-08-05 20:33 原文

G.Product EGF解决排列问题

题意

给定整数\(n,k,D\)

定义权值

\[\frac{D!}{\prod_{i=1}^n(a_i + k)!} \]

求出满足如下条件下的所有权值的和

  • 1.\(\forall i \in [1,n],a_i \geq 0\)
  • 2.\(\sum_{i=1}^na_i = D\)

\[1\leq n \leq 50\\ 0 \leq k \leq 50\\ 0 \leq D \leq 10^8 \]

分析

看到分母为阶乘的形式的数数题,尝试联想到\(EGF\)

Taylor级数

\[e^x = 1 + \frac{x^1}{1!}+\frac{x^2}{2!}+\frac{x^3}{3!}... \]

那么

\[\sum_{\sum a_i = D} \frac{1}{\prod_{i=1}^na_i!} = [x^D](e^x)^n \]

考虑到\(a_i + k \geq 0\)

\(b_i =a_i + k \geq k\) 也就是\(\leq k\)\(x^i\)不应该被计入贡献

\[\sum_{\sum b_i = D + nk}\frac{1}{\prod_{i=1}^n b_i!} = [x^{D+nk}](e^x - \sum_{i=0}^{k-1}\frac{x^i}{i!})^n \]

这个时候可以考虑做

\[(\sum_{i=k}^\infty\frac{x^i}{i!})^n \]

这样的话可以做多项式快速幂

但是也可以换一个思路 利用\(n,k\)较小的性质

\[(e^x-\sum_{i=0}^{k-1}\frac{x^i}{i!})^n = \sum_{i=0}^n (-1)^n\tbinom{n}{i}(\sum_{i=0}^{k-1}\frac{x^i}{i!})^i(e^x)^{n-i} \]

注意到可以预处理出\(\sum_{i=0}^{k-1}\frac{x^i}{i!}\)的幂次,次数最多会到\(nk\)级别

这样将二项式展开就可以做\(O(n^2k^2)\)的暴力卷积

代码

这类题做的不多 感觉实现有些细节需要仔细考虑

#include<bits/stdc++.h>
#define pii pair<ll,ll>
#define fi first
#define se second
using namespace std;
typedef long long ll;


inline int rd(){
	int x;
	scanf("%d",&x);
	return x;
}

const int MOD = 998244353;

inline int mul(int a,int b){
	int res = (ll)a * b % MOD;
	if(res < 0) res += MOD;
	return res;
}

inline void add(int &a,int b){
	a += b;
	if(a >= MOD) a -= MOD;
}

inline void sub(int &a,int b){
	a -= b;
	if(a < 0) a += MOD;
}

const int maxn = 55;

int fac[maxn],iv[maxn];
int C[maxn];

inline int ksm(int a,int b = MOD - 2,int m = MOD){
	int ans = 1;
	int base = a;
	while(b){
		if(b & 1) ans = (ll)ans * base % m;
		base = (ll)base * base % m;
		b >>= 1;
	}
	return ans;
}

map<pii,int> mp;

//a! / b!
inline int Ffac(int a,int b){
	//cout << a << ' ' << b << '\n';
	if(mp.count(make_pair(a,b))) return mp[make_pair(a,b)];
	int ans = 1;
	for(int i = b + 1;i <= a;i++)
		ans = mul(ans,i);
	for(int i = a + 1;i <= b;i++)
		ans = mul(ans,ksm(i));
	return mp[make_pair(a,b)] = ans;
}


int main(){
	fac[0] = 1;
	for(int i = 1;i < 55;i++)
		fac[i] = mul(fac[i - 1],i);
	iv[54] = ksm(fac[54]);
	for(int i = 53;i >= 0;i--)
		iv[i] = mul(iv[i + 1],i + 1);
	C[0] = 1;
	int n = rd();
	int k = rd();
	int D = rd();
	for(int i = 0;i < n;i++)
		C[i + 1] = mul(C[i],mul(n - i,ksm(i + 1)));
	vector<vector<int>> pol(n + 1);
	vector<int> t(k);
	for(int i = 0;i < k;i++){
		t[i] = iv[i];
	}
	pol[1] = t;
	for(int i = 2;i <= n;i++){
		pol[i].resize(k + pol[i - 1].size() - 1);
		for(int j = 0;j < k;j++){
			for(int h = 0;h < pol[i - 1].size();h++){
				add(pol[i][h + j],(ll)pol[i - 1][h] * t[j] % MOD);
			}
		}
	}
	/*
	if(k == 1){
		printf("%d",mul(ksm(n,D + n * k),Ffac(D,D + n * k)));
		return 0;
	}*/
	int ans = mul(ksm(n,D + n * k),Ffac(D,D + n * k));
	for(int i = 1;i <= n;i++){
		int res = 0;
		for(int j = 0;j < pol[i].size();j++){
			int Need = D + n * k - j;
			if(Need < 0) break;	
			add(res,mul(pol[i][j],mul(ksm(n - i,Need),Ffac(D,Need))));
		}
		res = mul(res,C[i]);
		if(i & 1) res = MOD - res;
		add(ans,res);
	}
	printf("%d",ans);
}

推荐阅读