题解 [TJOI2019] 唱、跳、rap和篮球

传送门

20 pts 好像能当成 GT 考试做
哦其实令一个 \(f_i\) 为确定了前 \(i\) 个,其中存在不合法的的方案数就能 \(O(n)\) 做了

然后正解:
我以为这玩意是不能二项式反演的其实可以
\(f_i\) 为恰好有 \(i\) 组,\(g_i\) 为至少 \(i\) 组的方案数
那么

\[g_i=\binom{n-3i}{i}h(n-4i, a-i, b-i, c-i, d-i) \]

其中 \(h(n, a, b, c, d)\) 表示无限制的放球方案数
上面那个组合数是在考虑将每一组缩成一个球
然后 \(h\) 怎么求呢?

  • \(m\) 种不同颜色的球,每种球有 \(a_i\) 个,求从中选出 \(n\) 个的方案数
    两种方案不同当且仅当至少存在一个位置在两种方案中放的球的颜色不同
    做法是 EGF 卷积,第 \(i\) 种颜色的生成函数是 \(R_i=\sum\limits_{j=0}^{a_i}\frac{x_j}{j!}\)

对于本题,发现每次每种颜色的变化量是很小的
那么可以从大到小枚举 \(i\),分别维护 \(R_{ab}, R_{cd}\),求一项时可以 \(O(n)\) 算出来
复杂度 \(O(n^2)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, a, b, c, d;
ll fac[N], inv[N], ans;
int alen, blen, clen, dlen, lim;
const ll mod=998244353, phi=mod-1;
ll fa[N], fb[N], fc[N], fd[N], fab[N], fcd[N], tem[N];
inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}

ll qval(int n) {
	ll ans=0;
	for (int i=0; i<=n; ++i) ans=(ans+fab[i]*fcd[n-i])%mod;
	return fac[n]*ans%mod;
}

signed main()
{
	n=read(); a=read(); b=read(); c=read(); d=read();
	fac[0]=fac[1]=1; inv[0]=inv[1]=1;
	lim=max(n, max(max(a, b), max(c, d)));
	for (int i=2; i<=lim; ++i) fac[i]=fac[i-1]*i%mod;
	for (int i=2; i<=lim; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for (int i=2; i<=lim; ++i) inv[i]=inv[i-1]*inv[i]%mod;
	fa[0]=fb[0]=fc[0]=fd[0]=fab[0]=fcd[0]=1;
	for (int i=min(min(min(a, b), min(c, d)), n/4); ~i; --i) {
		// cout<<"i: "<<i<<endl;
		while (alen<a-i) {
			++alen; fa[alen]=inv[alen];
			for (int j=0; j<=n-alen; ++j) fab[alen+j]=(fab[alen+j]+fa[alen]*fb[j])%mod;
		}
		while (blen<b-i) {
			++blen; fb[blen]=inv[blen];
			for (int j=0; j<=n-blen; ++j) fab[blen+j]=(fab[blen+j]+fb[blen]*fa[j])%mod;
		}
		while (clen<c-i) {
			++clen; fc[clen]=inv[clen];
			for (int j=0; j<=n-clen; ++j) fcd[clen+j]=(fcd[clen+j]+fc[clen]*fd[j])%mod;
		}
		while (dlen<d-i) {
			++dlen; fd[dlen]=inv[dlen];
			for (int j=0; j<=n-dlen; ++j) fcd[dlen+j]=(fcd[dlen+j]+fd[dlen]*fc[j])%mod;
		}
		ans=(ans+(i&1?-1:1)*C(n-3*i, i)*qval(n-4*i))%mod;
	}
	printf("%lld\n", (ans%mod+mod)%mod);

	return 0;
}
posted @ 2022-06-08 16:17  Administrator-09  阅读(4)  评论(0编辑  收藏  举报