强军如歌(strong)

强军如歌(strong)

题目描述

 

给定一个NN个数的序列AA,如果序列AA不是非降序的,你需要在其中选择一个数删掉,不断重复这个操作直到序列AA非降。求有多少种不同的删数方案。注意:删掉的数的集合相同,但是删数的顺序不同,视作不同的删数方案。

 

输入

 

第一行一个数NN。

接下来一行NN个正整数,用空格隔开,第ii个数表示Ai。

 

输出

 

一行一个整数表示答案对1000000007取模的结果。


solution

首先我们考虑不管删数顺序,枚举出剩下的不降序列。

假设长度为l

那么这时候的删数方案为(n-l)!

我们考虑一种不合法的删数方案:再删除第l个数前,序列一定要是不降的。

也就是每一个长度为l+1的不降序列,都对应着l+1个不合法方案。

用树状数组求出长度为l的不降子序列个数

这样就可以快速统计了

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 2005
#define mod 1000000007
#define ll long long
using namespace std;
int n,Max,a[maxn];
ll f[maxn][maxn],tree[maxn],ans[maxn],h[maxn];
int ask(int i){
    ll sum=0;
    for(;i;i-=i&-i)sum=(sum+tree[i])%mod;
    return sum;
}
void jia(int i,ll v){
    for(;i<=Max;i+=i&-i)tree[i]=(tree[i]+v)%mod;
}
int main()
{
    cin>>n;
    h[0]=1;for(int i=1;i<=n;i++)h[i]=(h[i-1]*i)%mod;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        Max=max(a[i],Max);
    }
    for(int i=1;i<=n;i++)f[i][1]=1;ans[1]=h[n];
    for(int i=2;i<=n;i++){
        for(int j=1;j<=Max;j++)tree[j]=0;
        for(int j=1;j<=n;j++){
            f[j][i]=ask(a[j]);
            jia(a[j],f[j][i-1]);
        }
        ll sum=0;
        for(int j=1;j<=n;j++)sum=(sum+f[j][i])%mod;
        ans[i]=(sum*h[n-i])%mod;
    }
    ll A=0;
    for(int i=1;i<=n;i++){
        A=A+ans[i]-(ans[i+1]*(i+1))%mod;A%=mod;
    }
    A=(A%mod+mod)%mod;
    cout<<A<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/liankewei/p/10358770.html