ARC138E Decreasing Subsequence
作者:互联网
第一步是巧妙的转化:对于 \(a_i>0\),连边 \((i,a_i-1)\)。
对于每个序列 \(a\),连边后形成若干条链。
假设选出的 \(k\) 个点为 \(b_1,b_2,...,b_k\),并且 \(b_1<b_2<...<b_k\),对应的 \(a_{b_1}-1,...,a_{b_k}-1\) 为了方便记作 \(c_1,c_2,...,c_k\),则有:
\[c_k<c_{k-1}<...<c_1<b_1<b_2<...<b_k \]这可以由 \(b_1>c_1\),\(c\) 递减,\(b\) 递增推导出来。
也就是说,在 \(a\) 确定的前提下,在 \([0,n]\) 中选出 \(2k\) 个点,它们对应的子序列有且仅有一种。
考虑枚举前 \(k\) 个点以及所在链上的其他点的个数 \(i\),后 \(k\) 个点以及所在链上的其他点的个数 \(j\),则方案数为:
\[\dbinom{n+1}{i+j}\begin{Bmatrix}i\\k\end{Bmatrix}\begin{Bmatrix}j\\k\end{Bmatrix}\sum\limits_{x=1}^{n+1-i-j}\begin{Bmatrix}n+1-i-j\\x\end{Bmatrix} \]将 \(i\) 个点分成 \(j\) 条链的方案数等价于第二类斯特林数,因为链上总是由编号大的指向编号小的。
注意一下实现做个前缀和就可以做到 \(\mathrm{O(n^2)}\)。
\(\texttt{Code:}\)
#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;
}
标签:return,int,cmd,Subsequence,maxn,Bmatrix,Decreasing,mul,ARC138E 来源: https://www.cnblogs.com/klii/p/16128517.html