bzoj 4361: isn

4361: isn

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 539  Solved: 269
[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][j] 为以第i位结尾,且目前非降子序列长度为j 的方案数。
显然 :
      1.g[i][1] = 1;
      2.g[i][j] = Σg[k][j-1]  ,其中 1<=k<i 且  a[k]<=a[i]。
然后这个玩意可以用树状数组优化,处理一下前缀就行了。
 
那么答案就是Σg[][i] * (n-i)! - g[][i+1] * (n-i-1)! * (i+1)。
为什么呢?
因为我们要减去不合法的方案,而最后删到剩k位的方案有g[][k] * (n-k)! 种,
但是其中有 g[][k+1] * (n-k-1)! * (k+1) 种是不合法的,因为合法的方案是删到剩k+1位时候还不是非降序列。
 
/*
    g[i][j]表示到第i为,上升长度为j的方案数  
    ans= g[i]*(n-i)! - g[i+1]*(n-i-1)!*(i+1)
*/
#include<bits/stdc++.h>
#define ll long long
#define maxn 2005
using namespace std;
const int ha=1000000007;
int num[maxn],ky;
int f[maxn][maxn],n,m;
int a[maxn],g[maxn][maxn],ans;

inline int add(int x,int y){
	x+=y;
	return x>=ha?x-ha:x;
}

inline void update(int lev,int x,int y){
	for(;x<=ky;x+=x&-x) f[lev][x]=add(f[lev][x],y);
}

inline int query(int lev,int x){
	int an=0;
	for(;x;x-=x&-x) an=add(an,f[lev][x]);
	return an;
}

inline void dp(){
	for(int i=1;i<=n;i++)
	    for(int j=i;j;j--){
	    	if(j>1) g[i][j]=query(j-1,a[i]);
	    	else g[i][j]=1;
	    	update(j,a[i],g[i][j]);
		} 
	
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++) g[0][j]=add(g[0][j],g[i][j]);
}

inline void calc(){
	int tmp=1,pre=1;
	for(int i=n;i;i--,pre=tmp,tmp=tmp*(ll)(n-i)%ha){
		ans=add(ans,add(g[0][i]*(ll)tmp%ha,ha-g[0][i+1]*(ll)pre%ha*(ll)(i+1)%ha));
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",a+i),num[i]=a[i];
	sort(num+1,num+n+1);
	ky=unique(num+1,num+n+1)-num-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(num+1,num+ky+1,a[i])-num;
	dp();
	calc();
	cout<<ans<<endl;
	return 0;
}

  

posted @ 2018-03-15 17:18  蒟蒻JHY  阅读(163)  评论(0编辑  收藏  举报