[关闭]
@ysner 2018-08-14T23:59:06.000000Z 字数 1527 阅读 2454

[bzoj4361]isn

DP 容斥 树状数组


题面

给出一个长度为的序列。如果序列A不是非降的,你必须从中删去一个数。
重复这一操作,直到非降为止。求有多少种不同的操作方案。

解析

首先肯定要离散化。(因为只关心相对大小)
于是数字范围为

我们需要求得,表示长度为的最长不下降子序列个数。
怎么求?
表示统计第个数字,得到最长不下降子序列末端为
(实际上,只有这样设状态才能转移)
显然这个状态可以从前面所有转移过来。
树状数组优化一下。
注意要防止统计到自己之前统计的状态。

考虑到在状态下,我们可以以任意顺序删去个数,则长度为的最长不下降子序列方案数为
但是,不能保证它在成为最长不下降子序列时就停止删数。
不合法方案数为(最后一次可以删个数中任意一个)。
倒着推,每次结果()都是合法的。
因而只要管第个数的情况就够了。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  11. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  12. using namespace std;
  13. const int N=2005,mod=1e9+7;
  14. ll dp[N][N],n,a[N],t[N],jc[N],o[N],len,f[N],ans;
  15. il void add(re int x,re ll w){for(re int i=x;i<=n;i+=i&-i) (t[i]+=w)%=mod;}
  16. 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;}
  17. il ll gi()
  18. {
  19. re ll x=0,t=1;
  20. re char ch=getchar();
  21. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  22. if(ch=='-') t=-1,ch=getchar();
  23. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  24. return x*t;
  25. }
  26. int main()
  27. {
  28. n=gi();
  29. fp(i,1,n) o[i]=a[i]=gi();o[n+1]=0;
  30. sort(o+1,o+2+n);len=unique(o+1,o+2+n)-o-1;
  31. fp(i,0,n) a[i]=lower_bound(o+1,o+1+len,a[i])-o;
  32. jc[0]=1;fp(i,1,n) jc[i]=(jc[i-1]*i)%mod;
  33. dp[0][0]=1;
  34. fp(j,1,n)
  35. {
  36. memset(t,0,sizeof(t));
  37. fp(i,1,n)
  38. {
  39. add(a[i-1],dp[i-1][j-1]);
  40. dp[i][j]=Query(a[i]);
  41. (f[j]+=dp[i][j])%=mod;
  42. }
  43. }
  44. (ans+=f[n])%=mod;
  45. 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;
  46. printf("%lld\n",ans);
  47. return 0;
  48. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注