1. 程式人生 > >bzoj4361 isn(樹狀數組優化dp+容斥)

bzoj4361 isn(樹狀數組優化dp+容斥)

algorithm 不同 urn 長度 span input 利用 main pre

4361: isn

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 938 Solved: 485
[Submit][Status][Discuss]

Description

給出一個長度為n的序列A(A1,A2...AN)。如果序列A不是非降的,你必須從中刪去一個數, 這一操作,直到A非降為止。求有多少種不同的操作方案,答案模10^9+7。

Input

第一行一個整數n。 接下來一行n個整數,描述A。

Output

一行一個整數,描述答案。

Sample Input

4
1 7 5 3

Sample Output

18

HINT

1<=N<=2000
設$g(i)$為數列中長度為$i$的非降序列個數,那麽我們可以利用容斥原理求得答案 $ans=\sum_{i=1}^{n}g(i)*(n-i)!-g(i+1)*(n-i-1)!*(i+1)$ $g(i)$中的不合法情況(已經是非降序列卻又再刪數)一定是從$g(i+1)$轉移來的,所以可以利用$g(i+1)$去掉$g(i)*(n-i)!$中的不合法情況 $g(i)$怎麽求呢 設$f(i,j)$以$i$結尾,長度為$j$的非降序列的個數 $f(i,j)=\sum_{k=1}^{i-1}f(k,j-1)*[A_k<=A_i]$ 但是這是$n^3$方的 於是我們用樹狀數組把$k$優化成$(logn)$ 復雜度為$O(n^2logn)$ $g(i)=\sum_{j=i}^{n}f(j,i)$
#include<iostream>
#include
<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; #define N 2005 const ll P=1e9+7; int n,m,A[N],B[N],p[N]; ll fac[N],s[N][N],f[N][N],g[N],ans; inline ll Md(ll a){return a<P?a:a-P;} void Add(int id,int x,ll v){for(;x<=n;x+=x&-x)s[id][x]=Md(s[id][x]+v);} ll Sum(
int id,int x){ll re=0; for(;x;x-=x&-x)re=Md(re+s[id][x]); return re;} void prep(){ fac[0]=1; for(ll i=1;i<=n;++i) scanf("%d",&A[i]),B[i]=A[i],fac[i]=fac[i-1]*i%P; sort(B+1,B+n+1); m=unique(B+1,B+n+1)-B-1; for(int i=1;i<=n;++i) p[i]=lower_bound(B+1,B+m+1,A[i])-B;//離散化 } int main(){ scanf("%d",&n); prep(); Add(0,1,1); for(int i=1;i<=n;++i) for(int j=i;j;--j) f[i][j]=Md(f[i][j]+Sum(j-1,p[i])),Add(j,p[i],f[i][j]); for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) g[i]=Md(g[i]+f[j][i]); for(ll i=1;i<=n;++i) ans=Md(Md(ans+g[i]*fac[n-i]%P)-g[i+1]*fac[n-i-1]%P*(i+1)%P+P); printf("%lld",ans); return 0; }

bzoj4361 isn(樹狀數組優化dp+容斥)