突破口:dp的阶段(按什么分步)
这里显然不好存每个颜色还剩余个数的这种暴力填每一位所需的状态,考虑按颜色填。
发现题目的性质,并证明其必要性,就是题目的等价条件了。
最后的合法串的判定等价于任何前缀的白色个数大于等于其它颜色种类数。
条件是:“任意+不等式”,即所有前缀 白色个数-其它颜色种类数
的最小值非负。
最小值显然在使前缀出现新颜色即每种其它颜色第一次出现处可能取到。
所以dp,设表示填了个白色,个其它颜色,有效状态满足。转移每次填最靠前的空位对应颜色。
点击查看代码
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2005;
const int M = N * N;
ll jc[M], ijc[M], dp[N][N];
ll ksm(ll a, ll b) {ll res(1); for(; b; b >>= 1, a = a * a % mod)if(b & 1)res = res * a % mod; return res;}
ll binom(int x, int y) {return jc[x] * ijc[y] % mod * ijc[x - y] % mod;}
void init(int up) {
jc[0] = 1; for(int i = 1; i <= up; i++) jc[i] = jc[i - 1] * i % mod;
ijc[up] = ksm(jc[up], mod - 2); for(int i = up; i; i--) {ijc[i - 1] = ijc[i] * i % mod;}
}
int main() {
int n, k, nk;
scanf("%d%d", &n, &k);
if(k == 1) {puts("1"); return 0;}
init(nk = n * k);
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= i; j++) {
dp[i][j] = dp[i - 1][j];
if(j) {dp[i][j] = (dp[i][j] + dp[i][j - 1] * (n - j + 1) % mod * binom(nk - i - (j - 1) * (k - 1) - 1, k - 2)) % mod;}
}
}
printf("%lld", dp[n][n]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2022-04-07 [SHOI2016]黑暗前的幻想乡