bzoj 4361: isn
4361: isn
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]表示长度为i的不下降子序列的个数,考虑到此时的序列可能是非法的(即在成为不下降序列是没有停下来),但是它肯定是从长度的i+1的过来的,考虑容斥原理,此时的方案为g[i]*(n-i)!-g[i+1]*(n-i-1)!*(i+1)
至于怎么求这个g数组,可以先求f[i][j],表示长度为i,结尾是a[j]的子序列个数
f[i][j]=Σf[i-1][k] (k<j&&a[k]<=a[j]),用树状数组维护。
#include<stdio.h> #include<iostream> #include<algorithm> using namespace std; const int M=1e9+7; const int N=2005; struct node { int a,id; }p[N]; int n,i,j,k,ans,a[N],h[N],f[N][N],fac[N],g[N],t[N]; bool cmp(const node&x,const node&y) { return x.a<y.a; } inline void update(int x,int y) { while(x<=k) { t[x]=(t[x]+y)%M; x+=x&-x; } } inline int solve(int x) { int ans=0; while(x>0) { ans=(ans+t[x])%M; x-=x&-x; } return ans; } int main() { scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&p[i].a),p[i].id=i; sort(p+1,p+n+1,cmp); k=0; p[0].a=-2e9; for(i=1;i<=n;i++) { if(p[i].a!=p[i-1].a) k++; h[p[i].id]=k; } fac[0]=fac[1]=1; for(i=2;i<=n;i++) fac[i]=1LL*fac[i-1]*i%M; for(i=1;i<=n;i++) f[1][i]=1; for(i=2;i<=n;i++) { for(j=1;j<=k;j++) t[j]=0; for(j=1;j<=n;j++) { f[i][j]=solve(h[j]); update(h[j],f[i-1][j]); } } for(i=1;i<=n;i++) for(j=1;j<=n;j++) g[i]=(g[i]+f[i][j])%M; ans=0; for(i=1;i<=n;i++) { ans=(ans+1LL*g[i]*fac[n-i]%M)%M; if(i<n) ans=(ans-1LL*g[i+1]*fac[n-i-1]%M*(i+1)%M+M)%M; } cout<<ans; return 0; }
一念起,天涯咫尺; 一念灭,咫尺天涯。