ARC138E Decreasing Subsequence

或许更好的阅读体验

link

第一步是巧妙的转化:对于 ai>0,连边 (i,ai1)

对于每个序列 a,连边后形成若干条链

假设选出的 k 个点为 b1,b2,...,bk,并且 b1<b2<...<bk,对应的 ab11,...,abk1 为了方便记作 c1,c2,...,ck,则有:

ck<ck1<...<c1<b1<b2<...<bk

这可以由 b1>c1c 递减,b 递增推导出来。

也就是说,在 a 确定的前提下,在 [0,n] 中选出 2k 个点,它们对应的子序列有且仅有一种

考虑枚举前 k 个点以及所在链上的其他点的个数 i,后 k 个点以及所在链上的其他点的个数 j,则方案数为:

(n+1i+j){ik}{jk}x=1n+1ij{n+1ijx}

i 个点分成 j 条链的方案数等价于第二类斯特林数,因为链上总是由编号大的指向编号小的。

注意一下实现做个前缀和就可以做到 O(n2)

Code:

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; }
posted @   klii  阅读(140)  评论(4编辑  收藏  举报
相关博文:
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起