CSP-S 模拟 长寿花 (组合数学)(DP)

40 篇文章 0 订阅
12 篇文章 0 订阅

传送门
N O I NOI NOI 模拟比 C S P CSP CSP 模拟简单干脆就都说成 C S P CSP CSP 模拟
显然按层转移,并且颜色种类没有关系,只关心个数
f i , j f_{i,j} fi,j 表示到第 i i i 层,有 j j j 种颜色的方案数
枚举填几种颜色, v a l ( i , j ) val(i,j) val(i,j) 表示 i i i 个空格 j j j 个颜色的方案数即 j ∗ ( j − 1 ) i − 1 j*(j-1)^{i-1} j(j1)i1
f i , j = ∑ k ! = j f i − 1 , k ∗ ( m k ) ∗ v a l ( a i , j ) + f i − 1 , j ∗ ( ( m j ) − 1 ) ∗ v a l ( a i , j ) f_{i,j}=\sum_{k!=j}f_{i-1,k}*\binom{m}{k}*val(a_i,j)+f_{i-1,j}*(\binom{m}{j}-1)*val(a_i,j) fi,j=k!=jfi1,k(km)val(ai,j)+fi1,j((jm)1)val(ai,j)
发现把多加的减掉即可
f i , j = s u m i − 1 ∗ ( m j ) ∗ v a l ( a i , j ) − f i − 1 , j ∗ v a l ( a i , j ) f_{i,j}=sum_{i-1}*\binom{m}{j}*val(a_i,j)-f_{i-1,j}*val(a_i,j) fi,j=sumi1(jm)val(ai,j)fi1,jval(ai,j)
然后发现模数任意,组合数已经凉了, e x l u c a s exlucas exlucas ???
有一个非常巧妙的做法是,我们考虑把组合数改成 m ! ( m − j ) ! \frac{m!}{(m-j)!} (mj)!m!,这样就可以简单预处理
然后发现多出来的 j ! j! j! 恰好是 v a l ( i , j ) val(i,j) val(i,j) 中 不考虑颜色顺序的方案数
由于 v a l ( i , j ) val(i,j) val(i,j) 不考虑颜色顺序,如果填新的颜色钦定是最大的颜色
那么有 v a l ( i , j ) = v a l ( i − 1 , j − 1 ) + v a l ( i − 1 , j ) ∗ ( j − 1 ) val(i,j)=val(i-1,j-1)+val(i-1,j)*(j-1) val(i,j)=val(i1,j1)+val(i1,j)(j1)
感觉好巧妙,通过修改定义解决了组合数的问题

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int Mod;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1, a=mul(a,a)) if(b&1) ans=mul(ans, a); return ans;}
cs int N = 1e6 + 5, M = 5e3 + 5;
int n, m, C[M], S[M][M];
vector<int> f[N];
int sum[N], a[N];
int g[N], fac[N];
int main(){
	n = read(), m = read(), Mod = read();
	g[0] = 1;
	for(int i = 1; i <= m; i++){
		g[i] = mul(g[i - 1], m - i + 1); 
	} 
	S[0][0] = 1;
	for(int i = 1; i <= M - 5; i++)
		for(int j = 1; j <= i; j++)
			S[i][j] = add(S[i - 1][j - 1], mul(j - 1, S[i - 1][j]));
	fac[0] = 1;
	for(int i = 1; i <= M - 5; i++) fac[i] = mul(fac[i-1], i);
	sum[0] = 1;
	for(int i = 1; i <= n; i++){
		a[i] = read(); 
		f[i].resize(a[i] + 1);
		for(int j = 1; j <= min(m, a[i]); j++){
			f[i][j] = mul(sum[i - 1], mul(g[j], S[a[i]][j]));
			if(j <= a[i - 1])
				f[i][j] = add(f[i][j], Mod - mul(fac[j], mul(S[a[i]][j], f[i - 1][j])));
			sum[i] = add(sum[i], f[i][j]);
		} 
	} cout << sum[n]; return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值