bzoj 4361: isn
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; }
我爱学习,学习使我快乐