[TJOI2019]唱、跳、rap和篮球

[TJOI2019]唱、跳、rap和篮球

重新拾起许久没有动过的生成函数大坑

Solution

我们套路地设\(g_k\)表示恰好有\(k\)个鸡你太美,\(f_k\)表示钦定有\(k\)个鸡你太美,那么不难得到\(g,f\)之间的关系

\[f_k = \sum_{i=k}\binom{i}{k}g_i \]

由二项式反演我们得到

\[g_k = \sum_{i=k}(-1)^{i-k}\binom{i}{k}f_k \]

在这个式子中,取\(k=0\)即可得到答案为

\[\sum_{k=0}(-1)^kf_k \]

我们想一下\(f_k\)怎么算,把每个鸡你太美当做一个位置,设\(S(a,b,c,d,n)\)表示随便乱放的方案数,则有

\[f_k=\binom{n-3k}{k}S(a-k,b-k,c-k,d-k,n-4k) \]

接下来只需考虑怎么求\(S\)

这是一个经典问题,由于要求有序,我们考虑只有第一种人的\(\text{EGF}\)

\[\sum_{k=0}^a {\frac{x^k}{k!}} \]

直接卷起来就行

这样子做的复杂度为\(O(n^2\log n)\),要做好多次\(\text{NTT}\),比较慢。但是我们看了一下,发现要求的\(S\)具有十分特殊的性质,\(a,b,c,d\)都对应成等差数列,所以可以分别在线维护前两个和后两个的卷积,查询时候\(O(n)\)暴力做,这样子就\(O(n^2)\)而且更好写了

Code

点我看代码(✺ω✺)
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
inline void read(LL &x) {
	x = 0; int f = 0; char ch = getchar();
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
	for(; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
	if(f) x = ~x + 1;
}
const LL P = 998244353;
const int N = 1010;
LL a[10], n, fac[N], ifac[N], f[N], g[N], tmp, ans, k;
inline LL fpow(LL x, int pnt) {
	LL res = 1;
	for(; pnt; pnt >>= 1, x = x * x % P) if(pnt & 1) res = res * x % P;
	return res;
}
int main() {
	read(n), read(a[1]), read(a[2]), read(a[3]), read(a[4]);
	fac[0] = 1; for(LL i = 1; i <= n; ++i) fac[i] = i * fac[i - 1] % P;
	ifac[n] = fpow(fac[n], P - 2); for(LL i = n - 1; i >= 0; --i) ifac[i] = ifac[i + 1] * (i + 1) % P;
 	sort(a + 1, a + 5);
	k = min(a[1], n / 4); for(int i = 1; i < 5; ++i) a[i] -= k; n -= 4 * k;
	for(int i = 0; i <= a[1]; ++i)
		for(int j = 0; j <= a[2]; ++j)
			(f[i + j] += ifac[i] * ifac[j]) %= P;
	for(int i = 0; i <= a[3]; ++i)
		for(int j = 0; j <= a[4]; ++j)
			(g[i + j] += ifac[i] * ifac[j]) %= P;
	for(; k >= 0; --k) {
		tmp = 0;
		for(int i = 0; i <= n; ++i) (tmp += f[i] * g[n - i] % P) %= P;
		tmp = tmp * fac[n] % P * fac[n + k] % P * ifac[k] % P * ifac[n] % P;
		(ans += ((k & 1) ? (P - tmp) : tmp)) %= P;
		for(int i = 1; i < 5; ++i) ++a[i]; n += 4;
		for(int i = 0; i <= a[1]; ++i) (f[i + a[2]] += ifac[i] * ifac[a[2]]) %= P;
		for(int i = 0; i <= a[2]; ++i) (f[i + a[1]] += ifac[i] * ifac[a[1]]) %= P;
		f[a[1] + a[2]] = (f[a[1] + a[2]] + P - ifac[a[1]] * ifac[a[2]] % P) % P;
		for(int i = 0; i <= a[3]; ++i) (g[i + a[4]] += ifac[i] * ifac[a[4]]) %= P;
		for(int i = 0; i <= a[4]; ++i) (g[i + a[3]] += ifac[i] * ifac[a[3]]) %= P;
		g[a[3] + a[4]] = (g[a[3] + a[4]] + P - ifac[a[3]] * ifac[a[4]] % P) % P;
	}
	printf("%lld\n",ans);
}
posted @ 2022-07-14 21:17  DCH233  阅读(85)  评论(0编辑  收藏  举报