题解:P10592 BZOJ4361 isn
很好很好的算答案方式。
小 D 有一个长度为
n 的序列。如果序列不是非降的,他会选择一个元素删去,直到序列变成非降的,这时停止操作。求操作序列种类数,对10^9+7 取模。两个操作序列不同,当且仅当它们长度不同或者某一次操作删除的元素位置不同。
这个问题的难点在于,如果此时序列已经非降了那么此时必须停止。我们考虑如果没有这个限制的问题:
小 D 有一个长度为
n 的序列。如果序列不是非降的,他会选择一个元素删去,直到序列变成非降的,这时可以选择停止操作,也可以不停止。求操作序列种类数,对10^9+7 取模。
我们考虑原序列的任意一个上升子序列对答案的影响,即有多少种将其它元素删除的方式,使得最终整个序列只剩这个子序列。
令这个上升子序列长度为
暴力枚举这个上升子序列仍然是指数复杂度。考虑 DP。
设
可以用树状数组轻易优化至
考虑统计答案。令
至此我们用
我们考虑原问题比弱化版的答案少在了哪些地方。即考虑一个上升子序列什么时候产生的贡献变小。
对于一个长度为
所以答案比弱化版少了:
暴力枚举
显然每个
所以答案为:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2024, P = 1e9 + 7;
int n, a[N], b[N];
int f[N][N]; // 以 i 结尾的长度为 j 的子序列数量
int g[N], nums;
struct Tree {
int tr[N];
void modify(int x, int d) {
for (int i = x; i <= nums; i += i & -i)
tr[i] = (tr[i] + d) % P;
}
int query(int x) {
int res = 0;
for (int i = x; i; i -= i & -i)
res = (res + tr[i]) % P;
return res;
}
}T[N];
int fac[N];
signed main() {
fac[0] = 1;
for (int i = 1; i < N; ++ i ) fac[i] = 1ll * fac[i - 1] * i % P;
cin >> n;
for (int i = 1; i <= n; ++ i ) {
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
nums = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++ i ) {
a[i] = lower_bound(b + 1, b + nums + 1, a[i]) - b;
}
for (int i = 1; i <= n; ++ i ) {
f[i][1] = 1;
for (int j = 2; j <= i; ++ j ) {
f[i][j] = T[j - 1].query(a[i]);
}
for (int j = 1; j <= i; ++ j ) {
T[j].modify(a[i], f[i][j]);
}
}
for (int i = 1; i <= n; ++ i )
for (int j = 1; j <= i; ++ j )
g[j] = (g[j] + 1ll * fac[n - j] * f[i][j] % P) % P;
int res = 0;
for (int i = 1; i <= n; ++ i )
res = ((res + g[i] - g[i + 1] * (i + 1) % P) % P + P) % P;
cout << res;
return 0;
}