Link
简要题意:称一个长为 2n−1 的排列 P 像堆,如果 Pi<P2i,且 Pi<P2i+1。给定 a,b,设 u=2a,v=2b+1−1,在所有像堆的排列中任取一个,求 Pu<Pv 的概率。
既然这个排列像堆,那就把这个问题放在满二叉树上解决。结点 i 的权值是 Pi,子结点是 2i 和 2i+1。
首先,如果把结点编号,然后按照权值排列,那么就是对这棵满二叉树做了一次拓扑排序,这里父结点向子结点连有向边。那么,我们只需要考虑结点的一个排列 S,使得 i 在其子树的前面。
题目中给定的 u,v 分别是第 a+1 层最左边的结点和第 b+1 层最右边的结点。而题目要求的 Pu<Pv 可以看作是从 u 向 v 连了一条有向边,或者说 S 中 u 必须在 v 前面。(下面的叙述中,涉及树的概念都是没有考虑这条边的)
这个拓扑序的开头当然是 1,然后整个图被分成了两棵满二叉树,从一棵的某个点向另一棵的某个点连边。
接下来,当然是选择一棵树的根结点。然后,这棵树又被分成了两棵树,其中有一棵是不含 u,v 之一的。那么,在安排好剩下的两棵树之后,将这棵树随意插入已有的排列即可。
想到这里,一个 DP 的方式呼之欲出:设 dpi,j 表示剩下一棵 i 层的满二叉树和一棵 j 层的满二叉树的方案数,则 dpi,j 会从 dpi−1,j 和 dpi,j−1 转移而来。注意,由于 u,v 距离叶子层的深度是不变的,所以这样的状态定义已经足够。再用 fi,j 表示相应的概率。
我们还需要考虑一个普通的 i 层满二叉树的拓扑序总数。设为 Si。
先求 Si 的递推式。第一步是选择根结点,然后是将两棵子树的所有排列方式放入拓扑序中。所有排列有 S2i−1 种。每棵子树的拓扑序长度为 2i−1−1,所以插入的方式有 C2i−1−12i−2 种。于是
Si=S2i−1×C2i−1−12i−2
设 u,v 分别在倒数第 A,B 层,则 DP 的初始值为 fA−1,j=1 对 j≥B 成立。
与前面类似可得 dpi,j 的递推式为:
dpi,j=dpi−1,j×Si−1×C2i−1−12i+2j−3+dpi,j−1×Sj−1×C2j−1−12i+2j−3
又由于
fi,j=dpi,jSi×Sj×C2i−12i+2j−2
所以
fi,j=dpi−1,j×Si−1×C2i−1−12i+2j−3+dpi,j−1×Sj−1×C2j−1−12i+2j−3Si×Sj×C2i−12i+2j−2
=fi−1,j×S2i−1×Sj×C2i−1−12i−1+2j−2×C2i−1−12i+2j−3Si×Sj×C2i−12i+2j−2+fi,j−1×S2j−1×Si×C2j−1−12i+2j−1−2×C2j−1−12i+2j−3Si×Sj×C2j−12i+2j−2
=fi−1,j×C2i−1−12i−1+2j−2×C2i−1−12i+2j−3C2i−1−12i−2×C2i−12i+2j−2+fi,j−1×C2j−1−12i+2j−1−2×C2j−1−12i+2j−3C2j−1−12j−2×C2j−12i+2j−2
结合
C2i−1−12i−1+2j−2×C2i−1−12i+2j−3C2i−1−12i−2×C2i−12i+2j−2
=(2i−1+2j−2)!×(2i+2j−3)!(2i−1−1)!×(2j−1)!×(2i−1−1)!×(2i−1+2j−2)!(2i−2)!×(2i+2j−2)!(2i−1−1)!×(2i−1−1)!×(2i−1)!×(2j−1)!
=(2i+2j−3)!×(2i−1)!(2i+2j−2)!×(2i−2)!
=2i−12i+2j−2
最后我们得到了一个漂亮的表达式:
fi,j=fi−1,j×2i−12i+2j−2+fi,j−1×2j−12i+2j−2
答案是 fn−1,n−1。
Code:
| #include<bits/stdc++.h> |
| using namespace std; |
| const int N=5005,mod=998244353; |
| int n,A,B; |
| long long pwr2[N],p[N][N],f[N][N]; |
| int power(int a,int b){ |
| int c=1; |
| for(;b;b>>=1){ |
| if(b&1)c=1ll*c*a%mod; |
| a=1ll*a*a%mod; |
| } |
| return c; |
| } |
| int main(){ |
| scanf("%d%d%d",&n,&A,&B); |
| A=n-A;B=n-B; |
| for(int i=1;i<=n;i++)pwr2[i]=power(2,i); |
| for(int i=1;i<=n;i++) |
| for(int j=1;j<=n;j++) |
| p[i][j]=(pwr2[i]-1)*power(pwr2[i]+pwr2[j]-2,mod-2)%mod; |
| for(int i=B;i<=n;i++)f[A-1][i]=1; |
| for(int i=A;i<=n;i++) |
| for(int j=B;j<=n;j++) |
| f[i][j]=(f[i-1][j]*p[i][j]%mod+f[i][j-1]*p[j][i]%mod)%mod; |
| printf("%d\n",f[n-1][n-1]); |
| return 0; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具