【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
Sample Output
18
HINT
1<=N<=2000
题解:想到动归+树状数组+容斥,但是容斥系数想复杂了~
我们希望先求出所有长度为i的非降序列个数,可以用DP解决。设f[i][j]表示最大值为i,长度为j的非降序列个数,用树状数组优化转移即可。
然后用g[i]表示$\sum f[..][j]$。因为其它数删除的顺序可以随便选,所以g[i]*=(n-i)!。但是有可能删到一半就已经得到了一个非降序列,怎么除去不合法状态呢?容斥呗!
一开始想了半天容斥系数,但其实g[i]-=g[i+1]*(i+1)即可。因为删到一般就得到非降序列情况刚好是g[i+1]。
时间复杂度$O(n\log n)$
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int P=1000000007; int n,m; ll ans; int val[2010],v[2010],p[2010]; int s[2010][2010]; ll f[2010],c[2010][2010],jc[2010],jcc[2010],ine[2010]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } bool cmp(const int &a,const int &b) { return val[a]<val[b]; } inline void updata(int x,int y,int z) { if(!z) return ; f[y]+=z; for(int i=x;i<=m;i+=i&-i) { s[y][i]+=z; if(s[y][i]>=P) s[y][i]-=P; } } inline int query(int x,int y) { int i,ret=0; for(i=x;i;i-=i&-i) { ret+=s[y][i]; if(ret>=P) ret-=P; } return ret; } int main() { n=rd(); int i,j; jc[0]=jc[1]=jcc[0]=jcc[1]=ine[0]=ine[1]=1; for(i=2;i<=n;i++) jc[i]=jc[i-1]*i%P,ine[i]=P-(P/i)*ine[P%i]%P,jcc[i]=jcc[i-1]*ine[i]%P; for(i=0;i<=n;i++) { c[i][0]=1; for(j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%P; } for(i=1;i<=n;i++) val[i]=rd(),p[i]=i; sort(p+1,p+n+1,cmp); for(i=1;i<=n;i++) { if(i==1||val[p[i]]>val[p[i-1]]) m++; v[p[i]]=m; } for(i=1;i<=n;i++) { for(j=i;j>=2;j--) updata(v[i],j,query(v[i],j-1)); updata(v[i],1,1); } for(i=1;i<=n;i++) f[i]=f[i]%P*jc[n-i]%P; for(i=1;i<=n;i++) f[i]=(f[i]-f[i+1]*(i+1)%P+P)%P,ans=(ans+f[i])%P; printf("%lld",ans); return 0; }//4 1 2 3 4
时间: 2024-10-05 23:55:18