数学期望 DP

https://notes.sshwy.name/Math/Expectation/Classic

https://ac.nowcoder.com/acm/contest/32282

对于一组离散型随机变量,出现其中某一变量的概率乘以这一变量值,再求和,就是数学期望。

也就是:

E(X)=i(p(X=i)×i)
通过这个定义,我们可以感知到,所谓期望,其实表示的是一组离散型随机变量的平均水平。 也可认为是进行某件事能得到的平均结果,或者理想代价。所以它也可以叫做一组离散型随机变量的均值。这也是期望这个概念的实际意义。

关于概率的一些性质:

  • 如果事件 AB 为互斥事件(不会既满足 A 又满足 B)那么 p(A||B)=p(A)+p(B)
  • 如果事件 AB 为独立事件(两个事件不相关)那么 p(A&&B)=p(A)×p(B)

关于期望的一些性质:

  • E(X+Y)=E(X)+E(Y)
    证明:E(X+Y)=i(jip(X=j&&Y=ij)×i)=jp(X=j)×j+k(k=ij)p(Y=k)×k=E(X)+E(Y)

  • E(XY)=E(X)E(Y),X,Y 相互独立
    证明:
    E(XY)=ijp(i=X&&j=Y)×i×j=ijp(i=X)i×p(j=Y)j=E(X)E(Y)

  • E(aX+b)=aE(X)+b

  • E(c)=c

  • 前缀和技巧:对于离散变量(取值只有整数)xP(x=k)=P(xk)P(xk1)

其中,当E(XY)=E(X)E(Y)的成立条件是 X,Y 相互独立(两个事件互不影响),这个式子反过来也是 X,Y 相互独立的判定条件。

以上是高中数学内容。在此重新提一下。

进行期望DP的时候,这些性质有时显得至关重要,可以帮助我们理解很多递推的转移。

ABC263E

题意:

n 个格子,在第 1n1 个格子上,第 i 个格子上有一个骰子,面值为 0ai,投一次骰子,这些面值均匀出现。投到几,就往前走几格。

1 号格子出发,求到 n 号格子所要投的骰子个数的数学期望。

分析:

对于线性期望 DP,通常有两种 DP 方式:

  1. dpi 表示从起始状态到 i 状态的数学期望。
  2. dpi 表示从 i 状态到起始状态的数学期望。

转移的时候,第一种方式通常从前往后转,需要起始状态已知。第二种方式通常是从后往前转,需要终止状态已知。有的时候这两种方式可以互换,有的时候不可以,需要灵活使用。

本题先考虑使用第 2 种方式,因为我们知道一个点可以前往哪些点,比较方便。
dpi 表示从 i 节点开始,到达 n 的数学期望。

先从有穷条件入手,即如果骰子可以投到 1ai(这样不会出现在原地打转的情况,比较符合拓扑序,方便入手),那么应该怎么算。
显然,

dpi=dpi+1+dpi+2+...+dpi+aiai+1

(投到 1,2,...,ai 每一种情况的概率是 1ai;不论什么情况从 i 都要多投 1 粒骰子)

如果是原题的无穷条件呢?在这时,虽然有些情况需要投掷的骰子数量会趋于无穷,但这时的概率成指数型增长。数学期望依然收敛。怎么计算呢?
照算不误。(by ajh 大佬)
我们假装 dpi 已经算好了。那么有以下式子,即为等量关系。所以可以划到同一边当作递推式。

dpi=dpi+dpi+1+dpi+2+...+dpi+aiai+1+1

dpi 提到左边有:

aiai+1dpi=dpi+1+dpi+2+...+dpi+aiai+1+1

那么,

dpi=ai+1ai(dpi+1+dpi+2+...+dpi+aiai+1+1)=dpi+1+dpi+2+...+dpi+ai+ai+1ai

时间复杂度 O(nlogmod),使用后缀和优化。

从另一种角度思考。考虑这个人投了平均 X 次骰子都是 0 之后才投到不是 0 的。
那么有: dpi=j=1aidpjai+X+1,其中 X 为在 i 处投到 0 的期望次数。(继承上面有穷条件的思路)
考虑求 X。画个树状图看看。
image
发现刚好0 次的概率为 aiai+1(前缀和技巧,至少 0 次 - 至少 1 次),刚好为 1 次的概率为 ai(ai+1)2,...,刚好为 j 次的概率为 ai(ai+1)j+1
那么 X=j=0j×ai(ai+1)j+1
这个东西怎么求呢?它是无穷幂级数。这东西有判敛法,改天学学。现在只要知道怎么计算即可。
错位相减法。等比数列求和中用过。这题怎么用?令 X=ai×S,x=(ai+1),则有:

S=(0x1+1x2+2x3+3x4+...)

xS=(0x0+1x1+2x2+3x3+...)

xSS=(1x1+1x2+1x3+1x4+...)

右边是等比数列,用前 n 项和公式计算:

(x1)S=1x×[1(1x)]11x

注意到 1x=0,化简得

S=1(x1)2

也就是

X=1ai2×ai=1ai

得到的式子和之前是一样的。
甚至我们可以直接猜出来在一个位置上期望投出 1ai 个骰子,这是有一个定理的:
【定理】
 p  1p 
证明和上面类似,套公式即可。
使用这个定理,“投出不是 0 的” 的概率是 aiai+1,那么期望第 ai+1ai 次发生。同样可以!

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1000000;
const int mod = 998244353;
int a[200010];
int dp[200010];
int suf[200010];
int qpow(int x, int k){
    int ans = 1;
    while(k) {
        if(k&1)ans=ans*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return ans;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //think twice,code once.
    //think once,debug forever.
    int n; cin >> n;
    f(i ,1,n)cin>>a[i];
    dp[n] = 0;
    for(int i = n-1;i >= 1; i--){
        int up = ((suf[i+1]-suf[i+a[i]+1])+a[i]+1+inf*mod) % mod;
        int down = a[i];
        dp[i] = up * qpow(down, mod - 2) % mod;
        suf[i] = (suf[i+1]+dp[i])%mod;
    }
    cout << dp[1] << endl;
    return 0;
}

拿球

  1. 箱子里有 n 个球 1,2,,n,你要从里面拿 m 次球,拿了后放回,求取出的数字之和的期望。
    一眼题,用到了 E(X+Y)=E(X)+E(Y) 的定律。
    E(xi)=E(xi)=(n+1)m2

  2. 箱子里有 n 个球 1,2,,n,你要从里面拿 m 次球,拿了后不放回,求取出的数字之和的期望。
    换个思路,设 xi 为:i,if i was chosen0,otherwise
    那么 S=i=1nxi
    那么 E(S)=E(xi)=E(xi)
    E(xi)=jp(xi=j)×j=p(xi=i)×i
    这个 p(xi=i) 就等于 mn,那么原式还是等于 (n+1)m2
    设置随机变量是非常重要的!这个问题中设置的是对答案的贡献。

  3. 箱子里有 n 个球 1,2,,n,你要从里面拿 m 次球,拿了后以 p1 的概率放回,p2 的概率放回两个和这个相同的球(相当于增加一个球),求取出的数字之和的期望。
    xi 为标号为 i 的球对答案的贡献,即被拿出来的次数乘以 i。设 yi 为被拿出来的次数。
    那么 E(S)=i=1nE(xi)=i=1nE(yi)×i
    我们需要求的是 E(yi)。这里不仅用了期望的线性加公式,还神来之笔地用到了 E(c)=c
    由于 E(y1)=E(y2)=...=E(yn) (地位均等)
    所以 E(y1)+E(y2)+...+E(yn)=E(y1+y2+...+yn)=E(m)=m
    E(y1)=mn
    那么代入原式得 E(S)=(n+1)m2

总结:这三种游戏,不管是放回还是不放回还是放回两个,都是地位均等的,结论都是 E(S)=(n+1)m2。另外我们需要巧妙寻找设置元素的方案,使得解题更快。

C

一个骰子有 m 面,第一个面有一个点,第二面有两个点,以此类推。Twilight Sparkle 确定投掷骰子时,每一面都是等概率出现的,即每面出现的概率为 1m。并且她知道每次投掷的结果是独立的。帮助她计算投掷 n 次骰子所能获得最大值的期望。

分析:

E(max{xi})=j=1mp(max{xi}=j)×j

前缀和套路,

p(max{xi}=j)=p(x1j&&x2j...&&x3j...xnj)p(x1<j&&x2<j...&&x3<j...xn<j)

概率独立可加性,

p(max{xi}=j)=p(x1j)+p(x2j)+...+p(xnj)p(x1<j)p(x2<j)...p(xn<j)=(jm)n(j1m)n

代回原式,得:

E(max{xi})=i=1mi((jm)n(j1m)n)

如果是可以取模,算到这里就行了,时间复杂度 O(mlogn)
不能取模的话把 sum 拆开来,发现是 m 减去一堆 (im)n,具体是:

mi=1m1(im)n

这东西先算 im 直接快速幂不会溢出。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
int m, n;
ld qpow(ld x, int k){
    ld ans=1;
    while(k){
        if(k&1)ans=ans*x;
        x=x*x;
        k>>=1;
    }
    return ans;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //think twice,code once.
    //think once,debug forever.
    cin >> m >> n;
    ld ans= m;
    f(i, 1, m-1){
        ans -= qpow((i*1.0/m), n);
    }
    cout << fixed<<setprecision(10)<<ans << endl;
    return 0;
}

CF1754E
题意:有一个 01 序列,每次可以选择两个元素,如果为逆序则交换,否则不变,无论是否交换都算一次操作。问排好序的期望操作次数。

分析:
两个环节,第一个环节可能需要灵感,第二个环节是套路。

状态设计,容易想到使用 DP 计算,但状态并不是很好想。首先状态必须有单向性,必须有严格的 DP 顺序,于是我们可以想到用逆序对数来记录状态。然而,思考会发现并不可行。不仅是复杂度无法接受,仅用逆序对也无法完整地表示状态。实际上,我们可以用“未归位的数字个数”来作为状态。具体地,假设当前序列为 0110101,该序列一共有三个 0,所以前三位应该都是 0,而实际上有两个 1,就定义此时的“未归位的数字个数”为 x=3。这个状态的单向性显然,完整性也容易证明,因为只有“归位”的交换是有意义的,如果没有达到“归位”的效果,无论是否交换都是没有意义的。

转移,首先考虑设计向前还是向后转。如果设计 dpi 表示走到有 i 个未归位的期望次数,那么 dpx 需要计算无穷级数不好转移。但是如果设计 dpi 表示走到有 i 个未归位之后期望还要几步,那么 dp0=0 就很好转移了。其次考虑转移方程式。最好用经典的“自己转到自己”的套路,并且牢记基本公式 E(X)=i(p(X=i)×i)。那么也就是:

dpi=2i2n(n1)×dpi1+n(n1)2i2n(n1)×dpi+1

稍微化简即可得到

dpi=dpi1+n(n1)2i2

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
const int mod = 998244353;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
int dp[3000010];
int qpow(int x, int k) {
	int ans  =1;
	while(k){
		if(k&1)ans=ans*x%mod;
		x=x*x%mod;
		k>>=1;
	}
	return ans;
}
int a[3000010];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    int T; cin >> T;
    while(T--) {
		int n; cin >> n;
		f(i, 0, n - 1) cin >> a[i];
		int cnt = 0;
		f(i, 0, n - 1) if(a[i] == 0) cnt++;
		int cw = 0;
		f(i, 0, cnt - 1) if(a[i] == 1) cw++;
		f(i, 1, cw) dp[i] = (dp[i - 1] + n * (n - 1) % mod * qpow(2 * i % mod * i % mod, mod - 2) % mod) % mod;
		cout << dp[cw] << endl; 
    }
    time_t finish = clock();
    //cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
}

P8774 爬树的甲壳虫

【题意】
有一只甲壳虫想要爬上一颗高度为 n 的树,它一开始位于树根, 高度为 0,当它尝试从高度 i1 爬到高度为 i 的位置时有 Pi 的概率会掉回树根, 求它从树根爬到树顶时, 经过的时间的期望值是多少。
n105
【分析】
dpi 表示还要爬多久。
有递推式 dpi=(1pi)×dpi+1+pi×dp0+1

推完递推式别傻愣着,看看怎么处理!

由于 dpn=0,考虑递推。

dpi=(1pi)×dpi+1+pi×dp0+1=(1p1)×((1pi+1dpi+2+pi+1dp0+1)+pidp0+1=...=Xidpn+Yidp0+Ci=Yidp0+Ci

考虑递推到 dp0,就可以出式子了。
Yi=(1pi)×Yi+1+pi
Ci=(1pi)×Ci+1+1

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
int x[100100], y[100010];
const int mod = 998244353;
int p[100010], q[100010], c[100010];
int qpow(int x, int k) {
    int ans = 1;
    while(k){
        if(k&1) ans=ans*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return ans;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    int n; cin >> n;
    f(i, 0, n - 1) {
        cin >> x[i] >> y[i];
        p[i] = x[i] * qpow(y[i], mod - 2) % mod;
    }
    q[n-1]=p[n-1];
    c[n - 1] = 1;
    for(int i = n - 2; i >= 0; i--) {
        q[i] = ((1 - p[i] + mod) % mod) * q[i + 1] % mod + p[i];
        q[i] %= mod;
        c[i] = (1 - p[i] + mod) %mod*c[i+1]%mod + 1;
    }
    cout << c[0] * qpow((1 - q[0] + mod) % mod, mod - 2) % mod;
    time_t finish = clock();
    //cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
} 

线性递推?观察式子!寻找出路!

相互推导又怎样?完全可以一起求。

P3232

【题意】
给定一个 n 个点 m 条边的无向连通图,顶点从 1 编号到 n,边从 1 编号到 m

小 Z 在该图上进行随机游走,初始时小 Z 在 1 号顶点,每一步小 Z 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 n 号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这 m 条边进行编号,使得小 Z 获得的总分的期望值最小。
【分析】
首先,求出每条边期望经过的次数,然后从大到小赋权值即可。考虑 gu,v 表示 uv 这条边期望被走到的次数。考虑转化为点的期望次数这样才好做,那么有:

gu,v=fueu+fvev

为什么呢?考虑走到 u 之后,有 1eu 的期望会走这条边。
其中 fu 表示 u 的期望经过次数,eu 表示 u 的边数。
考虑 fu 怎么求。

fu=uvfvdv

为什么呢?考虑走到 v 之后,有 1ev 的期望会走到这个点。
但是由于 n 在走到之后就不会再回来了。因此 fn 不能计入。可以考虑令 fn=0
1 刚开始必须走一次,而不是从别的地方走过来的。因此 f1 有些特殊,要加 1

然后得到了 n1 个元的线性方程组。考虑高斯消元。(又是一种处理多个变量同时推的式子的方案!)

UVA10288

【题意】
每张彩票上有一个漂亮图案,图案一共 n 种,如果你集齐了这 n 种图案就可以兑换大奖。

现在请问,在理想(平均)情况下,你买多少张彩票才能获得大奖的?
【分析】
首先,使用刚刚学过的 min-max 容斥可以发现答案为 k=1n(nk)×(1)k1×nk

其次,考虑集齐 k 种之后还需要 nk 次才能收集到下一种,那么答案为 k=1nnk
可以证明这俩等价。

P1654

【题意】
一共有 n 次操作,每次操作只有成功与失败之分,成功对应 1,失败对应 0n 次操作对应为 1 个长度为 n01 串。在这个串中连续的 X1 可以贡献 X3 的分数,这 X1 不能被其他连续的 1 所包含。

现在给出 n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留 1 位小数。
n105
【分析】
这是一道高次期望问题。为什么强调高次呢?因为期望的线性性:非独立的两个期望不可以相乘,必须转化为相加和数乘的形式。

dpi 表示以 i 结尾的 1 串长度的立方的期望。

考虑 dpi=pi×(3dpi1+1)3。这个式子并不成立。要转化为什么东西相加呢?

我们回到期望的本质。先观察一下平方的期望是什么意思。

E(leni2)=kP(leni=k)×kE(leni+12)=kP(leni=k)×(k+1)2×pi+1+0×(1pi+1)

为什么呢?考虑 E(leni+12) 应该等于 kP(leni+1=k)×k。其中 leni+1=leni+1 的概率是 pi+1,而 leni+1=0 的概率是 (1pi+1)。于是我们由定义和性质推出了递推式。

期望题一定要首先知道哪一个值是期望,期望打开是什么,而不是凭感觉随便乱推。这道题的题解里我没有使用 dp 而是使用了 E() 这个显式表达,就是要不忘初心,才能推的对式子。

接下来,由期望的线性性,我们继续推式子。

E(leni+12)=kP(leni=k)×(k+1)2×pi+1=(kP(leni=k)×k+kP(leni=k)×2k+kP(leni=k)×1)×pi+1=(E(leni2)+2E(leni)+1)×pi+1

这里注意 kP(leni=k)×1)=E(1)=1。(P=1

于是我们考虑维护 E(leni)E(leni2)。这个式子是这样推出来的,而不是靠玄学。

同样地,三次方的式子我们也可以推,结果是:

E(leni3)=(E(leni13)+3E(leni12)+3E(leni1)+1)×pi+1

CF1737E. Ela Goes Hiking

n 只蚂蚁站成一排,第 1 只蚂蚁左边和第 n 只蚂蚁右边各有一个挡板,相邻两只蚂蚁的距离、第 1 只蚂蚁与左边挡板的距离和第 n 只蚂蚁与右侧挡板的距离相等。初始时每只蚂蚁重量相等,每只蚂蚁有 12 概率向左运动,12 概率向右运动,每只蚂蚁速度相同。

蚂蚁之间会互相吃,一只蚂蚁吃掉另一只蚂蚁之后质量变为两只蚂蚁的质量之和,并保持速度和方向不变。方向改变当且仅当碰到了挡板,此时立刻改变方向。若两只蚂蚁相遇,重量大的蚂蚁会把重量小的蚂蚁吃掉,如果重量相同,向左运动的蚂蚁会吃掉向右运动的蚂蚁。求对于所有 i1n,第 i 只蚂蚁成为最终的存活者的概率对 109+7 取模。

n106


首先考虑一个弱化版的问题:对于一个特定的初始运动方向序列,结果是谁会赢?

考虑一开始向右走的所有蚂蚁都是在送,对于一串 R 后面一个 L,所有 R 都会被这个 L 吃掉。特别地,最后一个如果是 R,也相当于 L。因此可以把序列缩成一个全部向左走的串,其中每只蚂蚁的重量等于它前面连续 R 的个数。例如 RLRRLLRR=2312

然后,最前面的两只蚂蚁会打擂台似的一直决出一只继续走下去,例如 2312512628

编号为 k 的蚂蚁赢了,当且仅当:

  • k 的方向是 L,除非 k=n,此时 L/R 都行。
  • 缩起来的序列 b 中,假设 kbi,那么 bij=1i1bj
  • 对于 ii,不满足 bij=1i1bj

好。那么如今回到原题目来。我们会想到,对于条件 2,要求 bikbi,那么 2bik。因此,之前 k12 个数都必须是 R

条件 1 是好处理的。

那么条件 3 怎么办?考虑从后向前,减去后面能赢,并且这一位填 L 的就行了。也就是说,对于每一个后面的 j 赢的概率 pj,如果这一位可以填 L,那么这一位计算权值应该减去 pj2。注意是先减再乘,因为只有这样才能过样例先减再乘的话,说明减掉的概率应该在“已经保证之前 k12 个数都是 R 的全集”下。

进阶期望

期望题可以出的很牛逼,例如 P8967。这时候要灵活运用条件概率条件期望相关的东西。

这里复述一下相关公式和定理。

概念

p(A|B),E(A|B) 分别表示,当 B 成立时 A 的概率、期望。例如扔出两个骰子,考虑 x 为两个骰子的得数之和,y 为第一个骰子的得数,那么 p(x=7|y=4)=16E(x|y=4)=7.5

事件 AB,因为 A,B 独立的时候 p(AB)=p(A)×P(B),所以也会叫做事件 AB
事件 AB,因为 A,B 独立的时候 p(AB)=p(A)+P(B),所以也会叫做事件 A+B

公式

条件概率公式:p(A|B)=p(AB)P(B)

p(A|B)=p(A) 时,A,B 独立。(独立的定义)

全期望公式:E(A)=BE(A|B)p(B),B=Ω 需要保证。

现在我们结合 P8967 这个题来讲讲怎么应用这些公式求解东西。

P8967 追寻

【题意】

n 维空间中有一个梦想。这梦想坐落在 (d1,d2,,dn) 的地方。而你从 (0,0,,0) 开始,开启寻梦的旅程。

你的步伐轻缓,每一步只能走一个单位长度。你并不知道你的梦想位于哪里,所以你只能随机选择 n 个正方向中的一个,然后向这个方向走一步。也就是说,在 [1,n] 中均匀随机选择一个正整数 h,然后,使你在第 h 维的坐标变成原来的坐标加一。

然而,天有不测风云。在你走每一步的过程中,你会有 p=i=1kpi 的概率散入天际,并开始一段新的旅程。你会在 k 个地点中的一个重新开始这段旅程,其中第 i 个地点的坐标是 (ai,1,ai,2,,ai,n),从这里重新开始的概率为 pi

那么,期望下,你离到达这个梦想还需要多少步呢?

由于保证了 xi 是随机生成的,可以说明以接近 1 的概率答案在模意义下存在。事实上,一个当 xi 尚不确定时以合理地高的概率给出正确答案的算法足以通过本题,考察复杂的模意义下的有理数的处理不是我们的本意。

  • 1n1001k10000
  • di0idi107
  • 0ai,j107
  • xi1ixi<108。此即保证了 pi>0p<1
  • 保证存在一个 i[1,k] 使得对于每个 j[1,n] 均有 ai,jdj
  • 保证每个 (ai,1,ai,2,,ai,n) 作为空间中的点互不相同。
  • 保证每个 xi 在所有可能的组合中等概率随机生成。

【分析】

只描述正解。

关键点只有 ai。考虑让初始生成点为 a0
考虑每段路径都是形如:

a0a?a?...d

考虑i 次迷失之后,到达终点或第 i+1 次迷失之前走的期望路程叫做第 i 段路程。(这个已经很巧妙了,就是采用了分段求解的思想)
那么答案为 l=0infp( l )×E( l | l )

考虑分解下去做。

存在第 l 段路程,也就是迷失了 l 次,我们先考虑这个东西怎么算。

先考虑当我们在某个关键点重生(条件),走过这一段路程是迷失还是到终点的概率。显然这两个事件的概率和等于 1(条件概率的好处体现了:缩小概率空间的范围,使得全概率公式能用上)。考虑在第 i 个关键点重生,那么记 qi到终点了(注意,这里只是到终点的概率,虽然路程固定为 disi,但是需要满足两个因素:这些步不迷失,并且每一个维度走的是对的步数。注意不迷失不是条件!)的概率。

qi=(disidisi,1 disi,2 ... disi,n)ndisi×(1pi)disi

那么又迷失了的概率等于:1qi

ri=pip。因此迷失一次之后(条件,比上一个弱,上一个是:在 i 点重生。但是 ri 显然等于 Ω。),又迷失一次的概率 A 等于:

i=0kri(1qi)

那么

p( l )=(1q0)×Al1

接下来考虑 E( l | l ) 怎么求。显然是分两个情况。令 ti 表示确定这一次迷失的条件下的期望步数

E( l | l )=i=0kp()×(qidisi+(1qi)ti)

l=0 的时候,p()=[i=0],否则 p()=ri

只需求 ti 即可。

ti=step=1+inf(1pi)step1pi×step[step>disi]×qi×(1pi)stepdisi1pi×step

利用期望的线性性或者直接手算得到 1piqi×(1pi+disi)。(期望的线性性怎么用,乱走的减去到终点之后乱走一段的。这是高级思想,希望早日会用。)

那么就做完了。最后的式子是:

image

注意多项式定理的组合数预处理可以线性。足够通过本题。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
#define cerr if(false)cerr
#define freopen if(false)freopen
#define watch(x) cerr  << (#x) << ' '<<'i'<<'s'<<' ' << x << endl
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
const int mod = 998244353;
//调不出来给我对拍!
//n 100 k 10000 sum d_i 1e7 p = x / 10^8
int n,k; int d[110]; int a[10010][110]; int p[10010];
int A,B,ans; int q[10010]; int t[10010]; int dis[10010][110];
int jc[10000010], inv[10000010]; const int V = 1e7;
int qpow(int x,int y){
    int ret = 1; 
    while(y) {
        if(y&1) ret=ret*x%mod;
        x=x*x%mod; y>>=1;
    }
    return ret;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //freopen();
    //freopen();
    //time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    cin>>n>>k; f(i,1,n)cin>>d[i]; int div=qpow(100000000,mod-2);
    f(i,1,k){f(j,1,n){cin>>a[i][j];} cin>>p[i]; p[i]*=div; p[i]%=mod;}
    f(i,0,k)f(j,1,n){dis[i][j]=d[j]-a[i][j];dis[i][0]+=dis[i][j];}
    jc[0]=inv[0]=1;f(i,1,k){p[0]+=p[i]; p[0]%=mod;}int invp=1*qpow(p[0],mod-2);
    f(i,1,V+1) {jc[i]=jc[i-1]*i%mod;} inv[V+1]=qpow(jc[V+1],mod-2);
    for(int i=V;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;  
    f(i,0,k){
        bool keda=1; f(j,1,n){if(dis[i][j]<0)keda=0;}
        if(keda){q[i]=jc[dis[i][0]];f(j,1,n){q[i]*=inv[dis[i][j]]; q[i]%=mod;}
        q[i]*=qpow(n, dis[i][0] * (mod-2)); q[i]%=mod;//continue;
        q[i]*=qpow(1-p[0]+mod,dis[i][0]); q[i]%=mod; }
        t[i]=(invp-(q[i]*(invp+dis[i][0])%mod)+mod)%mod;
        t[i]*=qpow(1-q[i]+mod,mod-2); t[i]%=mod;
    }//return 0;
    f(i,1,k){A+=(p[i]*qpow(p[0],mod-2)%mod)*(1-q[i]+mod)%mod; A%=mod;}
    f(i,1,k){B+=(p[i]*qpow(p[0],mod-2)%mod)*(q[i]*dis[i][0]%mod+(1-q[i]+mod)*t[i]%mod)%mod;B%=mod;}
    ans=(1-q[0]+mod)*B%mod*qpow(1-A+mod,mod-2)%mod+q[0]*dis[0][0]%mod+(1-q[0]+mod)*t[0]%mod;
    cout<<ans%mod<<endl;
    //time_t finish = clock();
    //cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
}
/*
2023/x/xx
start thinking at h:mm


start coding at 11:17
finish debugging at h:mm
*/
posted @   OIer某罗  阅读(769)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示