4361: isn
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 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
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;
}