专栏文章
题解:AT_agc060_c [AGC060C] Large Heap
AT_agc060_c题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minl0bsy
- 此快照首次捕获于
- 2025/12/02 04:08 3 个月前
- 此快照最后确认于
- 2025/12/02 04:08 3 个月前
思路
题面要求排列需要满足“堆”的性质,那我们不妨直接把它看成一个满足小根堆性质的满二叉树。
前置
如何求满足小根堆性质的满二叉树的个数呢?
排列的总个数为 ,而每个节点需要满足必须小于他子树的所有节点,则需要除以 。
那么总方案为 。
容易观察 两点分别在树的左链和右链上,与其他无关。所以我们单独把左右链提出来,用类似于归并排序的思路从小到大加点。
令 为左链跳到 且右链跳到 时 的概率。
首先排除不合法的情况,即 的情况,因为是从小到大加点:当右链已经跳到 ,左链还没跳到 时。
接着考虑转移:
1.左链移动到 ,则满足 ,此时需要计算概率。 原本的子树个数为 ,合并后因为 ,子树个数要加上 的子树个数,为 。那么概率为 。
2.右链移动到 ,则满足 ,同理可得概率为 。
然后就做完了。
CPP#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define INF 1e12
#define cc cin
#define oo cout
#define nm tie(0)
using namespace std;
const int mod=998244353;
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int n,a,b;
int p[5005];
int dp[5005][5005];
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cc.nm;
oo.nm;
cin>>n>>a>>b;
a++,b++;
p[0]=1;
for(int i=1;i<=n;i++) p[i]=p[i-1]*2%mod;
dp[1][1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i<a&&j>=b) continue;
dp[i+1][j]+=(dp[i][j]*(p[n-i]-1)%mod*qpow(p[n-i]+p[n-j]-2,mod-2)%mod);
dp[i+1][j]%=mod;
dp[i][j+1]+=(dp[i][j]*(p[n-j]-1)%mod*qpow(p[n-i]+p[n-j]-2,mod-2)%mod);
dp[i][j+1]%=mod;
}
}
cout<<dp[n][n]<<"\n";
return 0;
}
/*
*/
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...