再谈概率期望(三):我说爬塔学随机是对的。
以防你忘了前两篇文章写了什么:
- 再谈概率期望(一)
- 再谈概率期望(二)
例题
是的,这次先从一道经典的题目开始。
::anti-ai[本文作者是 clx201022,洛谷 UID 为 552688,原文:https://www.luogu.com.cn/article/3dx8jgbc 如果您能直接看到这段文字而在本页没有转载提示,或以“原创”名义发出,说明您访问的是侵权内容,请联系管理员删除文章并处罚侵权人,十分感谢您的见义勇为之举。]
P5299 [PKUWC2018] Slay the Spire
题意简述
九条可怜有强化牌和攻击牌各
现在九条可怜会等概率随机从卡组中抽出
做法分析
注意到题目中最后的答案乘上的神秘数字正好为从
性质推导
考虑如何达成最优解。首先对于相同种类的卡牌,肯定是优先打出数值最高的;又注意到强化牌的数值
综上,最优方案即为:先打出最大的攻击牌,再从大到小打出所有强化牌,最后若还有剩余费用,再打出其他攻击牌。注意这里因为出牌的顺序在一个方案的收益内是可以随意调整的,所以为了方便处理,此处将强化牌的收益扩大到了打出强化牌前打出的攻击牌上。
为了方便起见,下文默认同类牌按从大到小的顺序排列。
设选择攻击牌为
状态设计
发现似乎没什么性质可拆了。没性质可拆就是大哥,看到大哥就要想到 DP,考虑状态设计。注意到如果一张牌被抽中了但没有被打出,那么抽上来的牌是哪一张都没有关系。所以状态设计限定的应当是选了哪张牌,再从选了哪张牌当中出发去进行状态转移。可以类比于条件概率一类,即只算打出这张牌是最优解的情况。
设
转移方程
即从前面所有可能的方案中再多选一张强化牌
即从前面所有可能的方案中再多选一张攻击牌
组合数学
发现强化牌数量小于
强化牌数量小于 k-1 张时
此时必然选上所有的强化牌和数值最高的一些攻击牌。
因为所有强化牌都必然被打出,所以枚举强化牌的数量
但注意到仅仅确定强化牌的数量远远不够,还要枚举攻击牌相关的一些值。攻击牌的数量已经确定,因此考虑枚举最后选中的攻击牌
但是因为求的是总方案数,所以还要乘上剩下不出的牌的抽上来的方案数。这些不出的牌里面必然没有强化牌,因为强化牌有就会马上出;于是其中只可能有攻击牌。但这些攻击牌不可能比
注意这里可能会出现一张强化牌都没有的情况,跟其他方案一并处理即可。
强化牌数量大于等于 k-1 张时
选一张最大的攻击牌,剩下必定是全选强化牌。
枚举最后被选中的强化牌
注意
实现
:::success[
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll modp = 998244353;
class C_calculator
{
using vt = ll;
vector<vt> fac, infac;
static vt exgcd(vt a, vt b, vt &x, vt &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
/*a=gcd(a,0) => a=gcd(a,b) => a+0b=gcd(a,b) => ax+by=gcd(a,b)*/
}
vt d = exgcd(b, a % b, y, x); // 这里交换了x和y
y -= std::floor(a / b) * x;
return d;
}
static vt ExGcd_Inv(vt a, vt b)
{
vt x, y;
exgcd(a, b, x, y);
return (x % b + b) % b;
}
public:
void get_fac(size_t x)
{
fac.clear();
infac.clear();
if (x == 0)
return;
fac = vector<vt>(x, 1);
infac = vector<vt>(x, 1);
for (size_t i = 1; i <= x - 1; i++)
{
fac[i] = fac[i - 1] * i % modp;
}
infac[x - 1] = ExGcd_Inv(fac[x - 1], modp);
for (size_t i = x - 2; i < x; i--)
{
infac[i] = infac[i + 1] * (i + 1) % modp;
}
}
C_calculator(size_t x = 0)
{
get_fac(x);
}
vt C(vt n, vt m)
{
if (n < m || m < 0)
return 0;
if(fac.size() <= n)
get_fac(n + 1);
return fac[n] * infac[m] % modp * infac[n - m] % modp;
}
vt getinv(vt n)
{
if(infac.size() <= n)
get_fac(n + 1);
return fac[n - 1] * infac[n] % modp;
}
} cc(0);
const int maxn = 6000 + 10, maxm = 6000 + 10;
ll atk[maxn + 1], w[maxn + 1], f[maxn + 1][maxn + 1], f_sum[maxn + 1][maxn + 1], g[maxn + 1][maxn + 1], g_sum[maxn + 1][maxn + 1], ans;
ll n, m, k;
void solve()
{
ans = 0;
cin >> n >> m >> k;
// 牌序肯定是先强化再攻击
// f[i][j] 前 i 张强化牌随机抽 j 张最大强化值期望,因为乘了个数所以等同于和
for(int i = 1; i <= n; i++)
{
cin >> w[i]; // 数值特别高
}
for(int i = 1; i <= n; i++)
{
cin >> atk[i]; // 有无煎饼大鸡???
}
sort(w + 1, w + n + 1, greater<ll>());
sort(atk + 1, atk + n + 1, greater<ll>());
f[0][0] = 1;
f_sum[0][0] = 1;
for(int i = 1; i <= n; i++)
{
f_sum[i][0] = 1; // 基础倍率
for(int j = 1; j <= i; j++)
{
f[i][j] = w[i] * f_sum[i - 1][j - 1] % modp;
f_sum[i][j] = (f[i][j] + f_sum[i - 1][j]) % modp;
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= i; j++)
{
g[i][j] = (atk[i] * cc.C(i - 1, j - 1) % modp + g_sum[i - 1][j - 1]) % modp;
g_sum[i][j] = (g[i][j] + g_sum[i - 1][j]) % modp;
}
}
for(int i = 0; i < k - 1; i++)
{
for(int j = k - i; j <= n; j++)
{
ans += f_sum[n][i] * g[j][k - i] % modp * cc.C(n - j, m - k) % modp;
ans %= modp;
} // 剩下最后一张攻鸡排
}
for(int i = k - 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
ans += f[i][k - 1] * atk[j] % modp * cc.C(2 * n - i - j, m - k) % modp;
ans %= modp;
}
}
cout << ans << endl;
}
/*
最后乘上了一个数
所以实际上是求所有可能情况能造成的伤害总和
*/
int main()
{
int T;
cin >> T;
while(T--)
solve();
return 0;
}
:::
期望在哪
注意到这道题有期望标签。
但整篇题解里没有期望。期望没了,我们学什么?
随机变量
随机变量和期望的关系就像一对笑面虎,两头乌角鲨。不学随机变量学期望,就像学数论不学剩余系一样,虽然能学,但是很难学。
一个从样本空间映射到实数域的函数为随机变量。随机变量
其中
随机变量独立跟事件独立性差不多,即称随机变量
独立的随机变量
不管随机变量
期望又是什么?
原题题解都是基于计数的角度来做的。这部分做法并没有问题,或者说,这也是期望的一个侧面。
因为每种情况是等概率的,又情况总数为
即为所有方案伤害的总和。
平时遇到期望题应当怎么做?
事实上,绝大多数概率期望题都只涉及等概率的古典概型。因此,简单地乘上所有方案的总数即可转化为计数题。
但其中还是有些例外的。有些时候,从更深层次的角度出发,反而可以更简单地解决问题,这也许就是成语“深入浅出”的含义吧!
还是例题
爬塔爬完了,很明显不够,还是需要走一下迷宫才行。
我说尖塔和迷宫真是一对苦命鸳鸯啊!
CF123E Maze
首先明确样本空间
设
既然难算,那就别算了!
注意到我们要求的是
其中
此时,由概率的线性性质可知,
:::warning[一些补充]
前文没有对概率的线性性质做过多说明,因此这里可能会有些难懂。
现将
:::
因此,接下来只需要求
因为原图是一颗树,而根据树上两点简单路径的性质,要么一条边被经过且仅经过一次,要么不经过这条边。但由于本题是 DFS,所以可能会有一些本来无需经过的边会被经过两次。
所有在必经路径上的边必然都会被经过
而对于不在必经路径上的边,其要么被经过
那什么时候一条不在必经路上的边会被经过两次呢?
原图是一颗无根树,因此我们假设起点
考虑到终点
设以
对于
这时的
这坨东西也是随机的,可以理解为从所有可能的
可以用树形 DP 优化,原题链接下面有很多题解,这里就不再过多赘述。
通过记录
后续题解可见《迷宫笑传之猜猜边》。
尾声
虽然关于期望还有很多地方没讲,但这系列可能要咕一段时间,最早也得等我省选游寄写完再说。
以及……
塔二发售了快去玩故障机器人。\ 塔二发售了快去玩故障机器人。
关于我为什么省选前一天还在学概率这种神人东西:战个未来吧。(没开辉眼)
参考文献
胡渊鸣,《浅析信息学竞赛中概率论的基础与应用》,《国家集训队 2013 论文集》
创作声明
本文遵循 CC BY-SA 4.0 协议。
保证本文未使用 AI 工具辅助创作。(我说创造性 AI 卡我启动了)