BZOJ4361 isn
题面
题解
有难度的计数$dp$
我们先求出所有不降子序列的个数
这个可以用树状数组维护
删除的总方案数为$(n-i)!$种
但是可能我们删到非降之后,我们可能还会删
那么设通过删除操作让子序列变成长度为$i$的方案数为$g[i]$,其中合法的有$f[i]$种
容斥:$f[i] = g[i] - g[i + 1]\times(i + 1)$
就可以啦
代码
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define clear(x, y) memset(x, y, sizeof(x))
inline int read()
{
int data = 0, w = 1; char ch = getchar();
while(ch != '-' && (!isdigit(ch))) ch = getchar();
if(ch == '-') w = -1, ch = getchar();
while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
return data * w;
}
const int maxn(2010), Mod(1e9 + 7), LIM(2000);
int f[maxn][maxn], c[maxn][maxn], fac[maxn], g[maxn], a[maxn], b[maxn], n, m;
inline int Add(int x, int y) { return (x + y) % Mod; }
void add(int a, int b, int c)
{
for(RG int i = b; i <= LIM; i += i & -i)
::c[a][i] = Add(::c[a][i], c);
}
int query(int a, int b)
{
int ans = 0;
for(RG int i = b; i; i -= i & -i)
ans = Add(ans, c[a][i]);
return ans;
}
int main()
{
n = read(), fac[0] = 1;
for(RG int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % Mod;
for(RG int i = 1; i <= n; i++) a[i] = b[i] = read();
std::sort(b + 1, b + n + 1);
m = std::unique(b + 1, b + n + 1) - b - 1;
for(RG int i = 1; i <= n; i++)
a[i] = std::lower_bound(b + 1, b + m + 1, a[i]) - b;
add(0, 1, 1);
for(RG int i = 1, t; i <= n; i++)
for(RG int j = i; j; j--)
t = query(j - 1, a[i]), f[a[i]][j] = Add(f[a[i]][j], t),
add(j, a[i], t);
for(RG int i = 1; i <= n; i++)
for(RG int j = 1; j <= m; j++)
g[i] = Add(g[i], f[j][i]);
int ans = 0;
for(RG int i = 1; i <= n; i++) g[i] = 1ll * fac[n - i] * g[i] % Mod;
for(RG int i = 1; i <= n; i++)
{
if(g[i + 1]) g[i] = (g[i] - 1ll * g[i + 1] * (i + 1) % Mod) % Mod;
if(g[i]) ans = Add(ans, g[i]);
}
if(ans < 0) ans += Mod;
printf("%d\n", ans);
return 0;
}