bzoj4361 isn(树状数组优化dp+容斥)

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;
}

 

posted @ 2019-04-29 20:46  kafuuchino  阅读(92)  评论(0编辑  收藏  举报