bzoj 4361: isn

4361: isn

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的不下降子序列的个数,考虑到此时的序列可能是非法的(即在成为不下降序列是没有停下来),但是它肯定是从长度的i+1的过来的,考虑容斥原理,此时的方案为g[i]*(n-i)!-g[i+1]*(n-i-1)!*(i+1)

至于怎么求这个g数组,可以先求f[i][j],表示长度为i,结尾是a[j]的子序列个数

f[i][j]=Σf[i-1][k]   (k<j&&a[k]<=a[j]),用树状数组维护。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e9+7;
const int N=2005;
struct node
{
    int a,id;
}p[N];
int n,i,j,k,ans,a[N],h[N],f[N][N],fac[N],g[N],t[N];
bool cmp(const node&x,const node&y)
{
    return x.a<y.a;
}
inline void update(int x,int y)
{
    while(x<=k)
    {
        t[x]=(t[x]+y)%M;
        x+=x&-x;
    }
}
inline int solve(int x)
{
    int ans=0;
    while(x>0)
    {
        ans=(ans+t[x])%M;
        x-=x&-x;
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&p[i].a),p[i].id=i;
    sort(p+1,p+n+1,cmp);
    k=0;
    p[0].a=-2e9;
    for(i=1;i<=n;i++)
    {
        if(p[i].a!=p[i-1].a) k++;
        h[p[i].id]=k;
    }
    fac[0]=fac[1]=1;
    for(i=2;i<=n;i++)
        fac[i]=1LL*fac[i-1]*i%M;
    for(i=1;i<=n;i++)
        f[1][i]=1;
    for(i=2;i<=n;i++)
    {
        for(j=1;j<=k;j++) t[j]=0;
        for(j=1;j<=n;j++)
        {
            f[i][j]=solve(h[j]);
            update(h[j],f[i-1][j]);
        }
    }
    for(i=1;i<=n;i++)
     for(j=1;j<=n;j++)
     g[i]=(g[i]+f[i][j])%M;
    ans=0;
    for(i=1;i<=n;i++)
    {
        ans=(ans+1LL*g[i]*fac[n-i]%M)%M;
        if(i<n) ans=(ans-1LL*g[i+1]*fac[n-i-1]%M*(i+1)%M+M)%M;
    }
    cout<<ans;
    return 0;
}

 

posted @ 2016-06-08 19:37  lwq12138  阅读(379)  评论(0编辑  收藏  举报