BZOJ4361 isn(动态规划+树状数组+容斥原理)

  首先dp出长度为i的不下降子序列个数,显然这可以树状数组做到O(n2logn)。

  考虑最后剩下的序列是什么,如果不管是否合法只是将序列删至只剩i个数,那么方案数显然是f[i]*(n-i)!。如果不合法,说明这个序列是由一个长度为i+1的非降序列删除一个数得来的,所以将其减去f[i+1]*(i+1)*(n-i-1)。这里的斥显然不会有重复。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 2010
#define P 1000000007
int n,a[N],b[N],f[N],tree[N][N],ans[N],fac[N],t,tot;
inline int inc(int &x,int y){x+=y;if (x>=P) x-=P;}
void add(int x,int k,int p){while (k<=t) inc(tree[x][k],p),k+=k&-k;}
int query(int x,int k){int s=0;while (k) inc(s,tree[x][k]),k-=k&-k;return s;}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4361.in","r",stdin);
    freopen("bzoj4361.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read();
    for (int i=1;i<=n;i++) b[i]=a[i]=read();
    sort(b+1,b+n+1);
    t=unique(b+1,b+n+1)-b-1;
    for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+t+1,a[i])-b;
    f[0]=1;add(0,1,1);
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=i;j++)
        inc(ans[j],f[j]=query(j-1,a[i]));
        for (int j=1;j<=i;j++)
        add(j,a[i],f[j]);
    }
    fac[0]=1;
    for (int i=1;i<=n;i++) fac[i]=1ll*i*fac[i-1]%P;
    for (int i=1;i<=n;i++)
    inc(tot,(1ll*ans[i]*fac[n-i]%P-1ll*ans[i+1]*fac[n-i-1]%P*(i+1)%P+P)%P);
    cout<<tot;
    return 0;
}

 

posted @ 2018-10-28 11:44  Gloid  阅读(258)  评论(0编辑  收藏  举报