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
正解:$dp$+树状数组。
感觉自己有点智商掉线,挺简单的一道题竟然没想出来。。
首先我们把所有方案数按照最后剩下的序列长度分类。
那么$ans=\sum_{i=1}^{n}f[i]*(n-i)!$,其中$f[i]$为长度为$i$的不降序列个数。
我们发现这样会算重,然后可以注意到算重的充要条件就是当这个序列的长度为$i+1$时就已经不降了。
所以再减去$f[i+1]*(i+1)*(n-i-1)!$即可。求$f$用一个简单的树状数组优化$dp$就行了。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define rhl (1000000007) 6 #define lb(x) (x & -x) 7 #define N (2005) 8 9 using namespace std; 10 11 int c[N][N],a[N],f[N],fac[N],hsh[N],n,tot,ans; 12 13 il int gi(){ 14 RG int x=0,q=1; RG char ch=getchar(); 15 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 16 if (ch=='-') q=-1,ch=getchar(); 17 while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); 18 return q*x; 19 } 20 21 il void add(RG int op,RG int x,RG int v){ 22 for (;x<=tot;x+=lb(x)){ 23 c[op][x]+=v; if (c[op][x]>=rhl) c[op][x]-=rhl; 24 } 25 return; 26 } 27 28 il int query(RG int op,RG int x){ 29 RG int res=0; 30 for (;x;x^=lb(x)){ 31 res+=c[op][x]; if (res>=rhl) res-=rhl; 32 } 33 return res; 34 } 35 36 int main(){ 37 #ifndef ONLINE_JUDGE 38 freopen("isn.in","r",stdin); 39 freopen("isn.out","w",stdout); 40 #endif 41 n=gi(),fac[0]=1; 42 for (RG int i=1;i<=n;++i) 43 hsh[i]=a[i]=gi(),fac[i]=1LL*fac[i-1]*i%rhl; 44 sort(hsh+1,hsh+n+1),tot=unique(hsh+1,hsh+n+1)-hsh-1; 45 for (RG int i=1;i<=n;++i){ 46 a[i]=lower_bound(hsh+1,hsh+tot+1,a[i])-hsh; 47 for (RG int j=i,tmp;j;--j){ 48 tmp=j==1?1:query(j-1,a[i]),add(j,a[i],tmp); 49 f[j]+=tmp; if (f[j]>=rhl) f[j]-=rhl; 50 } 51 } 52 for (RG int i=1;i<=n;++i){ 53 ans=(1LL*fac[n-i]*f[i]+ans)%rhl; 54 if (i<n) ans=(ans-1LL*fac[n-i-1]*f[i+1]%rhl*(i+1))%rhl; 55 } 56 cout<<(ans+rhl)%rhl; return 0; 57 }