【AGC002F】 Leftmost Ball 题解 (朴素 dp)

洛谷传送门 | AtC 传送门

朴素 dp

Solution

题面看起来很难——不同的两种排序方案,可能染色之后对应的是同种状态。

那就不妨反向考虑——从最终状态,倒退它能指向多少种初始状态。

根据题意,一种颜色对应着一个白球,这引导着我们从每种颜色的角度考虑,即每次考虑把当前颜色都放进去。

然后你就能根据强大的经验设计出 dp 的策略:从左往右放,每次对最左边的一个空位,要么放一个白球,要么放一个有颜色的球,同时把该种颜色剩下的球都放到后面的位置去。

即,定义 fi,j 表示当前有 i 个白球,并且已经把前 j 种颜色(假设我们把 k 中颜色的球按照颜色排了序)的球全部放好了的方案数。故当且仅当 ij 时,状态合法。

根据策略:要么放一个白球,要么放一种颜色,转移就很显然了。

注意答案最后要乘上 n!,表示颜色的不同排序方案。

Code

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
#define per(i, a, b) for(register int i = a; i >= b; --i)
const int maxn = 2005, mod = 1e9 + 7;
int f[maxn][maxn], n, k;
int fac[maxn * maxn], inv[maxn * maxn];

inline int pow(int x, int p){ int res = 1;
	while(p){ if(p & 1) res = res * x % mod;
		p /= 2, x = x * x % mod;
	} return res;
}
inline int C(int t, int m){
	if(t < m or t < 0 or m < 0) return 0;
	return fac[t] * inv[m] % mod * inv[t - m] % mod;
}

signed main(){ scanf("%lld%lld", &n, &k); 
	if(k == 1) return printf("1"), 0;
	fac[0] = 1; rep(i, 1, n * k) fac[i] = fac[i - 1] * i % mod;
	inv[n * k] = pow(fac[n * k], mod - 2);
	per(i, n * k - 1, 1) inv[i] = inv[i + 1] * (i + 1) % mod;
	f[0][0] = 1, inv[0] = 1;
	rep(i, 1, n) rep(j, 0, i){
		if(j <= i - 1) f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
		if(j) f[i][j] = (f[i][j] + C(n * k - (j - 1) * (k - 1) - i - 1, k - 2) * f[i][j - 1] % mod) % mod;
	} return printf("%lld", fac[n] * f[n][n] % mod), 0;
}

感谢阅读。

posted @   pldzy  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示