ARC138E Decreasing Subsequence
第一步是巧妙的转化:对于 ,连边 。
对于每个序列 ,连边后形成若干条链。
假设选出的 个点为 ,并且 ,对应的 为了方便记作 ,则有:
这可以由 , 递减, 递增推导出来。
也就是说,在 确定的前提下,在 中选出 个点,它们对应的子序列有且仅有一种。
考虑枚举前 个点以及所在链上的其他点的个数 ,后 个点以及所在链上的其他点的个数 ,则方案数为:
将 个点分成 条链的方案数等价于第二类斯特林数,因为链上总是由编号大的指向编号小的。
注意一下实现做个前缀和就可以做到 。
copy#include <bits/stdc++.h>
using namespace std;
const int cmd = 1e9 + 7;
inline int add(int a, int b) {a += b; return a < cmd ? a : a - cmd;}
inline int sub(int a, int b) {a -= b; return a < 0 ? a + cmd : a;}
inline int mul(int a, int b) {return 1ll * a * b % cmd;}
int fpow(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = mul(a, a))
if (b & 1) res = mul(res, a);
return res;
}
const int maxn = 5e3 + 5;
int n, k, S2[maxn][maxn], fac[maxn], ifac[maxn];
int C(int n, int m) {return mul(fac[n], mul(ifac[m], ifac[n - m]));}
int main() {
scanf("%d%d", &n, &k);
S2[1][1] = 1;
for (int i = 2; i <= n + 1; i++)
for (int j = 1; j <= i; j++)
S2[i][j] = add(S2[i - 1][j - 1], mul(S2[i - 1][j], j));
fac[0] = ifac[0] = 1;
for (int i = 1; i <= n + 1; i++) {
fac[i] = mul(fac[i - 1], i);
ifac[i] = fpow(fac[i], cmd - 2);
}
int ans = 0;
for (int i = k + k; i <= n + 1; i++) {
int s1 = 0, s2 = 0;
for (int j = k; j <= i - k; j++)
s1 = add(s1, mul(S2[j][k], S2[i - j][k]));
if (i < n + 1) {
for (int j = 1; j <= n + 1 - i; j++)
s2 = add(s2, S2[n + 1 - i][j]);
} else s2 = 1;
ans = add(ans, mul(s1, mul(s2, C(n + 1, i))));
}
printf("%d", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步