BZOJ4361 isn <容斥+DP+线段树优化>

Problem

isn

Time  Limit:  10  Sec\mathrm{Time\;Limit:\;10\;Sec}
Memory  Limit:  256  MB\mathrm{Memory\;Limit:\;256\;MB}

Description

给出一个长度为nn的序列A  (A1,A2An)A\;(A_1,A_2\cdots A_n)。如果序列AA不是非降的,你必须从中删去一个数,重复这一操作,直到AA非降为止。求有多少种不同的操作方案,答案模109+710^9+7

Input

第一行一个整数nn
接下来一行nn个整数,描述AA

Output

一行一个整数,描述答案。

Sample Input

1
2
4
1 7 5 3

Sample Output

1
18

Hint

1N20001\le N\le2000

标签:容斥 DP 线段树

Solution

f[i][j]f[i][j]表示以ii结尾长度为jj的不下降子序列个数,则

f[i][j]=k=1i1[akai]f[k][j1]f[i][j]=\sum_{k=1}^{i-1}[a_k\le a_i]\cdot f[k][j-1]

这个DP\mathrm{DP}n3n^3的,可以用线段树优化到O(n2logn)O(n^2\log{n})

g[i]g[i]表示长为ii的不下降子序列个数,则

g[j]=i=1nf[i][j]g[j]=\sum_{i=1}^{n}f[i][j]

h[i]h[i]为最后留下的不下降序列长度为ii可行方案数,那么显然有Ans=i=1nh[i]Ans=\sum_{i=1}^{n}h[i],考虑如何通过gg计算hh
如果不考虑方案是否可行,则留下的序列有g[i]g[i]种选法,删除nin-i个数有(ni)!(n-i)!种顺序,于是所有方案(不论是否可行)的数量为(ni)!×g[i](n-i)!\times g[i]
从所有方案中排除不可行的方案。观察可知这样的方案一定是在剩下一个长度为i+1i+1的不下降序列时多删除了一个数。剩下长度为i+1i+1的不下降序列的方案数为(ni1)!×g[i+1](n-i-1)!\times g[i+1],多删除的数有i+1i+1种选法,于是可知不可行的方案数为(ni1)!×g[i+1]×(i+1)(n-i-1)!\times g[i+1]\times (i+1)
综上可知,h[i]=(ni)!×g[i](ni1)!×g[i+1]×(i+1)h[i]=(n-i)!\times g[i]-(n-i-1)!\times g[i+1]\times (i+1)。然后就可以统计答案了。

总复杂度O(n2logn+n2+n)O(n^2\log{n}+n^2+n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
#define MAX_N 2000
#define P 1000000007
using namespace std;
typedef long long lnt;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
map <int, int> mp;
int n, m, a[MAX_N+5], b[MAX_N+5];
lnt f[MAX_N+5][MAX_N+5], g[MAX_N+5], ans;
lnt tr[MAX_N+5][MAX_N+5], pw[MAX_N+5] = {1};
void add(int id, int p, lnt x) {for (; p <= m; p += (p&-p)) (tr[id][p] += x) %= P;}
lnt sum(int id, int p) {lnt ret = 0; for (; p; p -= (p&-p)) (ret += tr[id][p]) %= P; return ret;}
int main() {
read(n);
for (int i = 1; i <= n; i++) pw[i] = pw[i-1]*i%P;
for (int i = 1; i <= n; i++) read(a[i]), b[i] = a[i];
sort(b+1, b+n+1), m = (int)(unique(b+1, b+n+1)-b-1);
for (int i = 1; i <= m; i++) mp[b[i]] = i;
for (int i = 1; i <= n; i++) a[i] = mp[a[i]];
f[0][0] = 1, add(0, 1, 1);
for (int i = 1; i <= n; i++)
for (int j = i; j; j--)
f[i][j] = sum(j-1, a[i]),
(g[j] += f[i][j]) %= P,
add(j, a[i], f[i][j]);
for (int i = 1; i <= n; i++)
(ans += pw[n-i]*g[i]%P) %= P,
(ans += P-pw[n-i-1]*g[i+1]%P*(i+1)%P) %= P;
return printf("%lld\n", ans), 0;
}