CF1392H ZS Shuffles Cards
一、题目
注意一点:重新洗牌并不会导致集合 的变化。
二、解法
本题的关键是均匀随机洗牌,可以有一个 : 中具体有哪些数字是没有关系的,我们只需要知道 中有多少数字。因为所有数字是全等概率出现的,我们关系的就只有出现和不出现这两种状态了。
然后可以考虑用 解决这个问题,直接 时间有点难,因为有废牌( 中已经出现的牌)所以转移不动。
考虑用轮数来介导这个过程,因为答案可以表示成 ,其中 表示第 轮的时间,可以拆成 设 表示期望要进行多少轮,那么答案是 ,其中 表示每一轮的期望时间。
那么设 表示 中还缺少 张牌的期望轮数,这时候就可以忽略废牌了,考虑抽到的第一张有用牌是什么,如果是 那么期望轮数 ,如果抽到了缺少的牌那么继续抽,显然抽到 的概率是 :
因为 满了之后还要抽到鬼才行,所以 ,那么
现在算 即可,我们考虑抽到鬼的时间,每张数字牌有 的概率出现在第一张鬼前面,每张数字牌是独立的,那么做的总贡献是 ,还要算上抽到自己的时间,所以
另一种方法是 反演,但是还是要以轮数为介导。
考虑给每个数字一个权值 ,表示加入 是第几轮。
设 表示一轮的期望时间,那么答案是 ,直接上反演:
考虑求 ,也就是一个集合中抽中第一张牌的轮数,每一轮抽出有用牌是 的概率是 ,那么期望轮数可以表示成 ,就是母函数的闭形式嘛,那么总答案是:
三、总结
核心原理是 ,所以期望不好算时要学会找介导量。
序列均匀随机有很多好的性质:比如元素等概率,顺序大小关系的概率会好算。
使用 反演的关键是定义权值,要求某些数全部出现是可以用,可以转化成某集合第一次出现数。
#include <cstdio>
#define int long long
const int MOD = 998244353;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,E,c;
int qkpow(int a,int b)
{
int r=1;
while(b>0)
{
if(b&1) r=r*a%MOD;
a=a*a%MOD;
b>>=1;
}
return r;
}
signed main()
{
n=read();m=read();
E=(n+m+1)*qkpow(m+1,MOD-2)%MOD;
for(int i=1;i<=n;i++)
c=(c+qkpow(i,MOD-2))%MOD;
c=(c*m+1)%MOD;
printf("%lld\n",E*c%MOD);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】