@ysner
2018-08-14T15:59:06.000000Z
字数 1527
阅读 3043
DP 容斥 树状数组
给出一个长度为的序列。如果序列A不是非降的,你必须从中删去一个数。
重复这一操作,直到非降为止。求有多少种不同的操作方案。
首先肯定要离散化。(因为只关心相对大小)
于是数字范围为。
我们需要求得,表示长度为的最长不下降子序列个数。
怎么求?
设表示统计第个数字,得到最长不下降子序列末端为。
(实际上,只有这样设状态才能转移)
显然这个状态可以从前面所有转移过来。
树状数组优化一下。
注意要防止统计到自己之前统计的状态。
考虑到在状态下,我们可以以任意顺序删去个数,则长度为的最长不下降子序列方案数为
但是,不能保证它在成为最长不下降子序列时就停止删数。
不合法方案数为(最后一次可以删个数中任意一个)。
倒着推,每次结果()都是合法的。
因而只要管第个数的情况就够了。
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define ll long long#define re register#define il inline#define fp(i,a,b) for(re int i=a;i<=b;i++)#define fq(i,a,b) for(re int i=a;i>=b;i--)using namespace std;const int N=2005,mod=1e9+7;ll dp[N][N],n,a[N],t[N],jc[N],o[N],len,f[N],ans;il void add(re int x,re ll w){for(re int i=x;i<=n;i+=i&-i) (t[i]+=w)%=mod;}il ll Query(re int x) {re ll res=0;for(re int i=x;i;i-=i&-i) (res+=t[i])%=mod;return res;}il ll gi(){re ll x=0,t=1;re char ch=getchar();while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') t=-1,ch=getchar();while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();return x*t;}int main(){n=gi();fp(i,1,n) o[i]=a[i]=gi();o[n+1]=0;sort(o+1,o+2+n);len=unique(o+1,o+2+n)-o-1;fp(i,0,n) a[i]=lower_bound(o+1,o+1+len,a[i])-o;jc[0]=1;fp(i,1,n) jc[i]=(jc[i-1]*i)%mod;dp[0][0]=1;fp(j,1,n){memset(t,0,sizeof(t));fp(i,1,n){add(a[i-1],dp[i-1][j-1]);dp[i][j]=Query(a[i]);(f[j]+=dp[i][j])%=mod;}}(ans+=f[n])%=mod;fq(i,n-1,1) ans=((ans+f[i]*jc[n-i]%mod-f[i+1]*jc[n-i-1]%mod*(i+1)%mod)+mod)%mod;printf("%lld\n",ans);return 0;}
