BZOJ4361 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
$f[i][j]$表示前i个数,不下降序列长为j的方案数
$F[j]=\sum_{i=1}^{n}f[i][j]$即不下降序列长为j的总方案数
但是会有重复,假设在长度为j+1时就已经构成了,那么长度为j的F[j]就会重复计算一部分
所以长度为i的方案:
$ans[i]=F[i]*fac[n-i]-F[i+1]*(i+1)*fac[n-i-1]$
这样DP要$O(n^3)$
用离散+树状数组(线段树)优化,复杂度$O(n^2logn)$
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 int Mod=1e9+7,n,a[2005],b[2005],m; 9 int c[2005][2005],fac[2005],tmp,F[2005],ans; 10 void update(int x,int p,int k) 11 { 12 while (x<=n) 13 { 14 c[p][x]+=k; 15 if (c[p][x]>=Mod) c[p][x]-=Mod; 16 x+=(x&(-x)); 17 } 18 } 19 int query(int x,int p) 20 { 21 int s=0; 22 while (x) 23 { 24 s+=c[p][x]; 25 if (s>=Mod) s-=Mod; 26 x-=(x&(-x)); 27 } 28 return s; 29 } 30 int main() 31 {int i,j; 32 cin>>n; 33 fac[0]=1; 34 for (i=1;i<=n;i++) 35 fac[i]=1ll*fac[i-1]*i%Mod; 36 for (i=1;i<=n;i++) 37 { 38 scanf("%d",&a[i]); 39 b[i]=a[i]; 40 } 41 sort(b+1,b+n+1); 42 m=unique(b+1,b+n+1)-b-1; 43 for (i=1;i<=n;i++) 44 a[i]=lower_bound(b+1,b+m+1,a[i])-b; 45 for (i=1;i<=n;i++) 46 { 47 for (j=i;j>=1;j--) 48 { 49 if (j==1) tmp=1; 50 else tmp=query(a[i],j-1); 51 update(a[i],j,tmp); 52 F[j]+=tmp; 53 if (F[j]>=Mod) F[j]-=Mod; 54 } 55 } 56 for (i=1;i<=n;i++) 57 { 58 ans+=1ll*F[i]*fac[n-i]%Mod; 59 if (ans>=Mod) ans-=Mod; 60 if (i<n) 61 ans=(ans-1ll*F[i+1]*fac[n-i-1]%Mod*(i+1)%Mod+Mod)%Mod; 62 } 63 cout<<ans; 64 }