[关闭]
@ysner 2018-07-24T13:08:50.000000Z 字数 2285 阅读 1888

序列

DP


题面

我们可以对任一序列构造笛卡尔树。笛卡尔树是一棵二叉树,它符合小根堆的性质,并且它的中序遍历就是该序列。如果序列中的数两两不同,那么笛卡尔树是唯一的。以下是一个笛卡尔树的例子:
图图
概括一下,就是每次在当前区间中选出值最小结点作为根,然后把根两边区间分别的最小值作为根的两个儿子,然后再分别取根两边区间向下递归
如此定义一棵笛卡尔树的值:

询问如果以所有的排列构建笛卡尔树,那么其中值不超过的个数。

吐嘈

认真表示此题和分组一题极为相似,我考场上竟然没切掉。。。
或许是因为我没有认真考虑排列的过程,而是认为排列是既成的。

解析

算法

我是不会承认我看了一小时图才发现正确建树方式的
暴枚排列+模拟建树,复杂度

算法

只有种取值,还这么小?
这是不是叫我们打表找规律。。。
时,;(
时,()
时,()
时,并不知道是什么规律。
于是回答就可以了。

算法

显然,建树是有一个过程的。
我们每给当前的排列从小到大一个数,就有几种决策:

依照决策,我们似乎能猜猜状态:表示当前排列有个数,值为,有个结点可匹配的方案数。
但是,这么就有一个问题,你并不知道新结点是与哪个结点匹配的,因而无法求得值。
我们可以用一种很套路的差分方法,就是新点接在叶子结点上时减去新点权值,等它被匹配时再加上匹配点的权值。
但是并不一定所有点都会被匹配啊!
所以,我们还要多一维状态维表示当前有个结点最后被匹配完毕的方案数(而个结点并不一定被匹配)。
第一个决策也要分为两类:新结点将匹配和新结点将不匹配。于是个决策。

第二个问题是按差分方法很有可能减着减着就成负数了。
想一想,每当我们加一个数,就相当于让所有的还未配对完毕的都加
因为它们要更晚才能被配对。
因此,每次转移时,即可。

好像新结点接在未匹配结点上的方案数也值得思考。
注意到,个结点能接个,而每加一个结点,占用了个能接的位置,又能接个。因此,每加一个结点,能接的位置+
个结点能接个。
而又有个位置是不能占的,因此方案数为

时间复杂度上限,空间复杂度滚动后

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #define re register
  8. #define il inline
  9. #define ll long long
  10. #define max(a,b) ((a)>(b)?(a):(b))
  11. #define min(a,b) ((a)<(b)?(a):(b))
  12. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  13. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  14. using namespace std;
  15. int n,m,mod,f[2][155][155][155],now,nxt=1;
  16. ll ans;
  17. bool vis[200];
  18. il ll gi()
  19. {
  20. re ll x=0,t=1;
  21. re char ch=getchar();
  22. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  23. if(ch=='-') t=-1,ch=getchar();
  24. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  25. return x*t;
  26. }
  27. int main()
  28. {
  29. freopen("seq.in","r",stdin);
  30. freopen("seq.out","w",stdout);
  31. n=gi();m=gi();mod=gi();
  32. f[1][0][0][0]=1;
  33. fp(i,1,n-1)
  34. {
  35. fp(j,0,m)
  36. fp(k,0,i)
  37. fp(l,k,i)
  38. {
  39. re int tmp=f[nxt][j][k][l],t=j+k;f[nxt][j][k][l]=0;
  40. if(!tmp||t>m) continue;
  41. if(k) (f[now][t][k-1][l-1]+=1ll*tmp*k%mod)%=mod;
  42. if(i+1-l>0) (f[now][t][k][l+1]+=1ll*tmp*(i+1-l)%mod)%=mod;
  43. if(i+1-l>0) (f[now][t][k+1][l+1]+=1ll*tmp*(i+1-l)%mod)%=mod;
  44. }
  45. swap(now,nxt);
  46. }
  47. fp(j,0,m)
  48. fp(l,0,n)
  49. (ans+=f[nxt][j][0][l])%=mod;
  50. printf("%lld\n",ans);
  51. fclose(stdin);
  52. fclose(stdout);
  53. return 0;
  54. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注