概率题技巧小结

技巧一: 势能函数

有一类题目,需要计算期望操作次数, 传奇OIer djq 给出了通用解法, 利用势能函数得出递推方程, 再根据 初末状态的势能差得出答案.

请添加图片描述

例题1: 951F. Company Acquisitions

n n n 个点, 它们会形成若干个高度 ≤ 2 \le 2 2 的有根树.

操作为: 随机选择两个根 A , B A,B A,B. 然后以相同概率 (1/2) 支配对方.

A A A 支配 B B B, 则 B B B 变为 A A A 的儿子, 而原本 B B B 的儿子们变成独立的(单点)有根树. 如下图:

在这里插入图片描述
在这里插入图片描述

分析: 设 f ( x ) f(x) f(x) 为一棵 第二层有 x x x 个节点的有根树的势能函数.

设此时有 k k k 棵树, 第二层节点数分别为 x 1 , x 2 , … , x k x_1,x_2, \dots, x_k x1,x2,,xk.
∑ i = 1 k f ( x i ) = 1 ( 1 次操作 ) + ∑ i ( k − 2 k f ( x i ) + ∑ j ≠ i 1 2 k ∗ ( k − 1 ) ( f ( x i + 1 ) + x j ∗ f ( 0 ) + f ( x j + 1 ) + x i ∗ f ( 0 ) ) ) ∑ 2 k f ( x i ) = 1 + ∑ i 1 k ∗ ( x i f ( 0 ) + f ( x i + 1 ) ) ∑ f ( x i ) = ∑ i ( 1 + x i f ( 0 ) + f ( x i + 1 ) ) / 2 f ( x + 1 ) = 2 f ( x ) − 1 − x f ( 0 ) what’s   f(0)?   For convenience, let f(0) = 0.(势能可以随便设置) \sum_{i=1}^k f(x_i) = 1(1次操作) +\sum_i (\dfrac{k - 2} k f(x_i) + \sum_{j \ne i} \dfrac 1{2k * (k - 1)} (f(x_i + 1) + x_j*f(0) + f(x_j + 1) + x_i*f(0))) \\ \sum \dfrac{2} k f(x_i) =1 + \sum_i \dfrac{1} k* (x_if(0)+f(x_i+1)) \\ \sum f(x_i) = \sum_i (1+x_if(0) + f(x_i+1))/2 \\ f(x+1) = 2f(x)-1-xf(0) \\ \text{what's ~ f(0)? ~~For convenience, let f(0) = 0.(势能可以随便设置)} i=1kf(xi)=1(1次操作)+i(kk2f(xi)+j=i2k(k1)1(f(xi+1)+xjf(0)+f(xj+1)+xif(0)))k2f(xi)=1+ik1(xif(0)+f(xi+1))f(xi)=i(1+xif(0)+f(xi+1))/2f(x+1)=2f(x)1xf(0)what’s   f(0)?   For convenience, let f(0) = 0.(势能可以随便设置)

直接递推即可. 最后的目标状态势能是 f ( n − 1 ) f(n-1) f(n1).

int n, m, d[N];
ll f[N];

void solve() {
    qr(n);
    int x;
    FOR(i, n) {
        qr(x);
        if(~x) d[x]++, d[i] = -1; // degree
    }
    f[0] = 0;
    rep(i, 0, n) {
        f[i + 1] = (2 * f[i] - i * f[0] - 1) % mod;
        dl(f[i + 1], 0);
    }
    int ans = 0;
    FOR(i, n) if(~d[i]) ad(ans, f[d[i]]); // 初始状态势能
    dl(ans, f[n - 1]); // 末状态势能
    pr2(ans);
}

例题2: 1349D. Slime and Biscuits

n n n 个人玩传饼干游戏, 一开始第 i i i 个人有 a i a_i ai 块.

每轮在 ∑ i = 1 n a i \sum_{i=1}^n a_i i=1nai 块中均匀随机出一块, 让其主人再 等概率发给其他 n − 1 n-1 n1 个人之一.

如果一个人拥有了全部的饼干, 游戏结束.

n ≤ 1 0 5 , m = ∑ i = 1 n a i ≤ 3 ∗ 1 0 5 n \le 10^5, m=\sum_{i=1}^n a_i \le 3*10^5 n105,m=i=1nai3105.

f ( x ) f(x) f(x) 表示一个人拥有了 x x x 块饼干的势能.

则有
f ( a ) = ∑ i = 1 n f ( a i ) n = 1 n ∑ i = 1 n 1 + a i m ( f ( a i − 1 ) + ∑ j ≠ i ( n − 2 n − 1 f ( a j ) + 1 n − 1 f ( a j + 1 ) ) ) ∑ i = 1 n f ( a i ) = ∑ i = 1 n ( a i m ( f ( a i − 1 ) + m a i ) + ( m − a i ) ( n − 2 ) m ( n − 1 ) f ( a i ) + m − a i m ( n − 1 ) f ( a i + 1 ) ) ) ) l e t   x = a i , f ( x ) − f ( x + 1 ) = ( n − 1 ) m − x ( x ( f ( x − 1 ) − f ( x ) ) − m ) f(a) = \dfrac{\sum_{i=1}^n f(a_i)}n = \dfrac 1 n\sum_{i=1}^n 1 +\dfrac {a_i}m (f(a_i-1) + \sum_{j\ne i} (\dfrac {n-2}{n-1} f(a_j) + \dfrac {1}{n-1}f(a_j+1))) \\ \sum_{i=1}^n f(a_i) = \sum_{i=1}^n (\dfrac {a_i}m (f(a_i-1)+\dfrac m{a_i}) + \dfrac {(m-a_i)(n-2)}{m(n-1)} f(a_i) + \dfrac {m-a_i}{m(n-1)}f(a_i+1))) )\\ let ~ x=a_i, f(x)-f(x+1) = \dfrac{(n-1)}{m-x} (x(f(x-1) - f(x))-m) f(a)=ni=1nf(ai)=n1i=1n1+mai(f(ai1)+j=i(n1n2f(aj)+n11f(aj+1)))i=1nf(ai)=i=1n(mai(f(ai1)+aim)+m(n1)(mai)(n2)f(ai)+m(n1)maif(ai+1))))let x=ai,f(x)f(x+1)=mx(n1)(x(f(x1)f(x))m)
边界: f ( m ) = 0 , f ( 0 ) − f ( 1 ) = n − 1 f(m)=0, f(0)-f(1)=n-1 f(m)=0,f(0)f(1)=n1.

原因: 结束位置势能最低; 一个0饼干的位置得到一个饼干的概率为 1 n − 1 \dfrac 1 {n-1} n11, 故期望要 n − 1 n-1 n1 步才能得到一块.

Code:

const int N = 3e5 + 10;
ll n, m, a[N], f[N];

void solve() {
    qr(n); m = 0;
    FOR(i, n) qr(a[i]), m += a[i];
    // let f(m) = 0. m是势能最低点
    f[0] = n - 1; // f(0) - f(1)
    FOR(i, m - 1) f[i] = (n - 1) % mod * power(m - i, mod - 2, mod) % mod * (f[i - 1] * i % mod + m) % mod;
    REP(i, 0, m - 1) ad(f[i], f[i + 1]);   
    ll ans = 0;
    FOR(i, n) ad(ans, f[a[i]]);
    // dl(ans, f[m]);  // f(m) = 0
    dl(ans, (n - 1) * f[0] % mod);
    pr2(ans * power(n, mod - 2, mod) % mod);
}

例题3: 1951G - Clacking Balls

有围成环形的 m m m 个篮子, n n n 个球( m ≥ n m\ge n mn).

操作:

  • 每次随机一个 i ∈ [ 1 , n ] i\in [1,n] i[1,n], 如果球 i i i 不存在,则无变化.
  • 否则, 令球 i i i 移动到下一个篮子, 若该篮子有球,则丢弃球 i i i.

结束状态: 只剩一个球.

n ≤ 3 ∗ 1 0 5 , m ≤ 1 0 9 n\le 3*10^5,m\le 10^9 n3105,m109.

设当前剩余 k k k 个球, 每个球距离前一个球的距离为 a i a_i ai,

f ( A ) = f ( a 1 , a 2 , . . . , a k ) = 1 + n − k n f ( A ) + 1 n ∑ i = 1 k f ( a 1 , . . . , a i + 1 , a i + 1 − 1 , a i + 2 , . . . ) f(A)=f(a_1,a_2,...,a_k)=1+\dfrac {n-k} n f(A)+\dfrac 1 n\sum_{i=1}^k f(a_1,...,a_i+1, a_{i+1}-1,a_{i+2},...) f(A)=f(a1,a2,...,ak)=1+nnkf(A)+n1i=1kf(a1,...,ai+1,ai+11,ai+2,...).

为了方便,记 f ( A + { i } ) = f ( a 1 , . . . , a i + 1 , a i + 1 − 1 , a i + 2 , . . . ) f(A+\{i\}) = f(a_1,...,a_i+1, a_{i+1}-1,a_{i+2},...) f(A+{i})=f(a1,...,ai+1,ai+11,ai+2,...).


k f ( A ) = n + ∑ i = 1 k f ( A + { i } ) n = ∑ i = 1 k f ( A ) − f ( A + { i } ) = d e f ∑ i = 1 k h ( A , i ) kf(A)=n+\sum_{i=1}^k f(A+\{i\}) \\ n=\sum_{i=1}^k f(A)-f(A+\{i\}) {\overset{def} {=}}\sum_{i=1}^k h(A,i) kf(A)=n+i=1kf(A+{i})n=i=1kf(A)f(A+{i})=defi=1kh(A,i)

f ( A ) = ∑ i = 1 k ∑ i = 0 a i g ( i ) f(A)=\sum_{i=1}^k \sum_{i=0}^{a_i} g(i) f(A)=i=1ki=0aig(i)(解构序列为元素的叠加, 抛弃顺序信息), 则 n = ∑ i = 1 k g ( a i ) − g ( a i + 1 ) n=\sum_{i=1}^k g(a_i)-g(a_i+1) n=i=1kg(ai)g(ai+1).

为了让 部分 a i = 0 , k = n a_i=0,k=n ai=0,k=n 时上式恒成立, 我们令 g ( 0 ) = g ( 1 ) = 0 g(0)=g(1)=0 g(0)=g(1)=0.

注意到 ∑ a i = m \sum a_i= m ai=m, 故可令

g ( x + 1 ) − g ( x ) = − n m x → g ( x ) = − n m ( x 2 ) → s g ( a ) = ∑ i = 0 a g ( i ) = − n m ( a + 1 3 ) g(x+1)-g(x) = -\dfrac n m x \rightarrow g(x) = -\dfrac n m \dbinom x 2 \rightarrow sg(a)=\sum_{i=0}^a g(i)=-\dfrac n m \dbinom {a+1} 3 g(x+1)g(x)=mnxg(x)=mn(2x)sg(a)=i=0ag(i)=mn(3a+1)

故势能函数为 f ( A ) = ∑ i = 1 n s g ( a i ) f(A)=\sum_{i=1}^n sg(a_i) f(A)=i=1nsg(ai). 答案为 f ( A ) − f ( ( m ) ) = n m ( ( m + 1 3 ) − ∑ i = 1 n ( a i + 1 3 ) ) f(A)-f((m))=\dfrac n m (\binom {m+1}3- \sum_{i=1}^n \binom {a_i+1}3 ) f(A)f((m))=mn((3m+1)i=1n(3ai+1))

const int N = 3e5 + 10, inv6 = power(6, mod - 2, mod);
int n, m, a[N];

ll f(ll x) {
    x++; return x * (x - 1) % mod * (x - 2) % mod * inv6 % mod;
}

void solve() {
    qr(n, m);
    FOR(i, n) qr(a[i]);
    sort(a + 1, a + n + 1);
    a[0] = a[n] - m;
    ll ans = f(m);
    FOR(i, n) dl(ans, f(a[i] - a[i - 1]));
    pr2(ans * n % mod * power(m, mod - 2, mod) % mod);
}

习题:

总结:

本做法需要有极强的 找通解 能力, 需要一定的观察力和解耦函数能力.

技巧二: 随机排列

这个技巧的实现方式是:

  1. 初始化最优解 集合(序列) S ∗ = ∅ S^*=\empty S=, 设置循环次数 T T T.(一般要充分卡时,设置得尽量大)

  2. 随机一个排列 P P P, 维护一个合法集合 S = ∅ S=\empty S=,顺序遍历 P P P, 看 P i P_i Pi 是否能加入 S S S, 如果加入合法则加入( S ← S ∪ { P i } S\leftarrow S \cup \{P_i\} SS{Pi}).

  3. 判断 S S S 是否比 S ∗ S^* S 更优, 若是, 则更新 S ∗ = S S^*=S S=S.

    1. --T>0, 重复操作2.
    2. 否则, 结束求解.(认为找到了最优解)

如果 ∣ P ∣ |P| P 比较小, 那么可以直接全排列. 否则 T = TL(s) ∗ 1 0 8 / F ( n ) T=\text{TL(s)} * 10^8 /F(n) T=TL(s)108/F(n), F ( n ) F(n) F(n) 为计算一次答案的复杂度.

技巧三: 纳什均衡

纳什均衡是非合作 、信息透明(知道其他参与方决策分布)的博弈.

对于每个参与方满足 u i ( s i ∗ , s − i ∗ ) ≥ u i ( s i , s − i ∗ )      f o r    a l l      s i ∈ S i {\displaystyle u_{i}(s_{i}^{*},s_{-i}^{*})\geq u_{i}(s_{i},s_{-i}^{*})\;\;{\rm {for\;all}}\;\;s_{i}\in S_{i}} ui(si,si)ui(si,si)forallsiSi, 其中 s ∗ = ( s i ∗ , s − i ∗ ) {\displaystyle s^{*}=(s_{i}^{*},s_{-i}^{*})} s=(si,si) 是一个纳什均衡点, s i ∈ S i s_i \in S_i siSi 表示第 i i i 个人的决策, u i u_i ui 表示第 i i i 个人的收益.

严格纳什均衡: u i ( s i ∗ , s − i ∗ ) > u i ( s i , s − i ∗ )      f o r    a l l      s i ∈ S i , s i ≠ s i ∗ {\displaystyle u_{i}(s_{i}^{*},s_{-i}^{*})>u_{i}(s_{i},s_{-i}^{*})\;\;{\rm {for\;all}}\;\;s_{i}\in S_{i},s_{i}\neq s_{i}^{*}} ui(si,si)>ui(si,si)forallsiSi,si=si, 保证了纳什均衡点的唯一.

总结: 纳什均衡是只谋私利, 不求共赢的.(囚徒困境…)

例题1: 1912F - Fugitive Frenzy

警察在一棵树上追捕逃犯,在 0 0 0 时刻,警察出现在编号为 s s s 的顶节点,而逃犯则会选择一个节点出现,之后从警察开始,他们会轮流行动。

  • 每次轮到警察移动时,他可以任意选择一个与当前节点相邻的节点并移动过去。警察移动的时间为一个单位时间。此外,警察也可以决定静止不动,在这种情况下,他会在开始移动的顶点等待一个单位时间。如果在移动结束时,警察与逃犯出现在同一个节点,他会立即抓住逃犯,追捕结束。
  • 每次轮到逃犯移动时,假设他位于点 b b b ,而警察位于点 p p p 。逃犯会选择一个 p p p 不在 b b b b ′ b' b 的路径上的点 b ′ b' b 并立即移动到 b ′ b' b,他的移动不需要花费时间,且他也可以选择停留在原地。

逃犯每时每刻都知道警官的位置(包括 s s s )。相反,警官对逃犯的行踪一无所知,只有在抓住逃犯的那一刻才能发现他。

警察的目标是尽快抓住逃犯,而逃犯的目标是尽可能晚地被抓住,在警察和逃犯都采取最优行动的情况下,求追捕时间的期望。

2 ≤ n ≤ 100 2 \le n \le 100 2n100

首先, 逃犯一定会跑到叶子, 警察也只会往叶子跑. 当警察到达叶子后, 逃犯重新选择叶子节点.

现在求双方的纳什均衡点. 设 p u , v , q u , v p_{u,v},q_{u,v} pu,v,qu,v 分别表示 s = u s=u s=u 时到警察到 v v v的概率, 逃犯到 v v v 的概率,

x u x_u xu 为警察在 u u u 时的期望追捕时间, 叶子集合为 L L L.

显然有 ∀ u ≠ v , v ∈ L , p u , v , q u , v > 0 \forall u\ne v,v\in L, p_{u,v},q_{u,v}>0 u=v,vL,pu,v,qu,v>0.

x u x_u xu 的转移方程, 双方梯度:
x u = ∑ v ∈ L \ { u } F ( u , v ) w h e r e   F ( u , v ) = p u , v ( d i s t   ( u , v ) + ( 1 − q u , v ) x v ) . F o r   p o l i c e : P u ( v )   =   ∂ ∂ p u . v F ( u , v )   =   ( d i s t   ( u , v ) + ( 1 − q u . v ) x v ) F o r   f u g i t i v e : Q u ( v ) = ∂ ∂ q u , v F ( u , v ) = − p u , v x v x_{u}=\sum_{v\in{\cal L}\backslash\{u\}}F(u,v)\qquad{\mathrm{where~}}F(u,v)=p_{u,v}({\mathrm{dist}}\,(u,v)+(1-q_{u,v})x_{v}). \\ For~ police: P_{u}(v)\:=\:\frac{\partial}{\partial p_{u.v}}F(u,v)\:=\:(\mathrm{dist}\:(u,v)+(1-q_{u.v})x_{v}) \\ For~fugitive: Q_u(v) = \frac ∂{∂q_{u,v}} F(u,v) = −p_{u,v}x_v xu=vL\{u}F(u,v)where F(u,v)=pu,v(dist(u,v)+(1qu,v)xv).For police:Pu(v)=pu.vF(u,v)=(dist(u,v)+(1qu.v)xv)For fugitive:Qu(v)=qu,vF(u,v)=pu,vxv
v ∈ L ∖ { u } v\in L \setminus \{u\} vL{u}, P u ( v ) P_u(v) Pu(v) 的值都应该相等, 否则警察选择调高梯度小的一侧会更优. 同理 Q u ( v ) Q_u(v) Qu(v) 对一个 u u u 也应该都相等.

1 = ∑ w ∈ L \ { u } p u , w = ∑ w ∈ L \ { u } p u , w x w ⋅ 1 x w = ∑ w ∈ L \ { u } p u , v x v ⋅ 1 x w = p u , v x v ⋅ ∑ w ∈ L \ { u } 1 x w . 1=\sum_{w\in L\backslash\{u\}}p_{u,w}=\sum_{w\in L\backslash\{u\}}p_{u,w}x_{w}\cdot{\frac{1}{x_{w}}}=\sum_{w\in L\backslash\{u\}}p_{u,v}x_{v}\cdot{\frac{1}{x_{w}}}=p_{u,v}x_{v}\cdot\sum_{w\in L\backslash\{u\}}{\frac{1}{x_{w}}}. 1=wL\{u}pu,w=wL\{u}pu,wxwxw1=wL\{u}pu,vxvxw1=pu,vxvwL\{u}xw1.

p u , v = 1 / x v ∑ w ∈ L \ { u } 1 / x w . \large p_{u,v}={\frac{1/x_{v}}{\sum_{w\in L\backslash\{u\}}1/x_{w}}}. pu,v=wL\{u}1/xw1/xv.

由于 p p p 的纳什平衡点不依赖与 q q q, 所以我们不妨令 q u , v = [ v = t ] q_{u,v}=[v=t] qu,v=[v=t] , 即 逃犯只会往 t t t 点跑.
F ( u , v ) = 1 / x v ∑ w ∈ L ∧ { u } 1 / x w ( d i s t   ( u , v ) + ( 1 − q u , v ) x v ) . F ( u , v ) = 1 / x v ∑ w ∈ L \ { u } 1 / x w ( d i s t   ( u , v ) + x v − [ v = t ] x t ) . x u = ∣ L ∖ { u } ∣ − 1 + ∑ w ∈ L \ { u } d i s ( u , w ) ∑ w ∈ L \ { u }     1 / x w x u ⋅ ∑ v ∈ L \ { u } 1 / x v = ∣ L ∣ − 2 + [ u ∉ L ] + ∑ v ∈ L \ { u } d i s t   ( u , v ) / x v . 设 u ∈ L , 将上式两边加 1. x u ⋅ ∑ v ∈ L 1 / x v = ∣ L ∣ − 1 + ∑ v ∈ L d i s t ( u , v ) / x v . x u = ∣ L ∣ − 1 + ∑ v ∈ L d i s t ( u , v ) / x v ∑ v ∈ L 1 / x v . 设 y u = 1 / x u , y u = ∑ v ∈ L y v ∣ L ∣ − 1 + ∑ v ∈ L d i s t ( u , v ) y v . F(u,v)=\frac{1/x_{v}}{\sum_{w\in L\land\{u\}}1/x_{w}}(\mathrm{dist}\,(u,v)+(1-q_{u,v})x_{v}).\\ F(u,v)=\frac{1/x_{v}}{\sum_{w\in L\backslash\{u\}}1/x_{w}}(\mathrm{dist}\ (u,v)+x_{v}-[v=t]x_{t}).\\ x_{u}=\frac{|L\setminus\{u\}|-1 +\sum_{w\in L\backslash\{u\}}dis(u,w)} {\sum_{w\in L\backslash\{u\}\ ~~1/x_{w}}} \\ x_{u}\cdot\sum_{v\in L\backslash\{u\}}1/x_{v}=|L|-2+[u\not \in L]+\sum_{v\in L\backslash\{u\}}\mathrm{dist}\,(u,v)/x_{v}. \\ 设 u\in L, 将上式两边加1. \\ x_{u}\cdot\sum_{v\in L}1/x_{v}=|L|-1+\sum_{v\in L}\mathrm{dist}\left(u,v\right)/x_{v}. \\ x_{u}=\dfrac{|L|-1+\sum_{v\in L}\mathrm{dist}\left(u,v\right)/x_{v}}{\sum_{v\in L}1/x_{v}}. \\ 设 y_u=1/x_u, y_{u}=\frac{\sum_{v\in L}y_{v}}{\vert L\vert-1+\sum_{v\in L}\mathrm{dist}\left(u,v\right)y_{v}}. F(u,v)=wL{u}1/xw1/xv(dist(u,v)+(1qu,v)xv).F(u,v)=wL\{u}1/xw1/xv(dist (u,v)+xv[v=t]xt).xu=wL\{u}   1/xwL{u}1+wL\{u}dis(u,w)xuvL\{u}1/xv=L2+[uL]+vL\{u}dist(u,v)/xv.uL,将上式两边加1.xuvL1/xv=L1+vLdist(u,v)/xv.xu=vL1/xvL1+vLdist(u,v)/xv.yu=1/xu,yu=L1+vLdist(u,v)yvvLyv.
用上述 y u y_u yu 的式子迭代即可. 计算完所有叶子节点的 y y y 后, 我们再求 x s x_s xs.

复杂度 O ( n ℓ + q ℓ 2 + n ℓ ) = O ( n ℓ + q ℓ 2 ) , w h e r e   n = ∣ V ∣ , ℓ = ∣ L ∣   a n d   q   i s   t h e   n u m b e r   o f   i t e r a t i o n s O(n\ell+q\ell^{2}+n\ell)=O\left(n\ell+q\ell^{2}\right),{\mathrm{where~}}n=|V|,\ell=|L|{\mathrm{~and~}}q{\mathrm{~is~the~number~of~iterations}} O(n+q2+n)=O(n+q2),where n=V,=L and q is the number of iterations

初始化 y u = 1 ( n − 1 ) 2 y_u=\dfrac 1 {(n-1)^2} yu=(n1)21 , q q q 取 10000 即可轻松通过本题.

const int N = 102;
int n, m, id[N], A[N][N], d[N];
ld y[2][N];
VT<int> e[N];

void bfs(int x) {
    fill_n(d, n, n);
    d[x] = 0;
    queue<int> q;
    q.push(x);
    while(q.size()) {
        int x = q.front(); q.pop();
        for(int y: e[x]) if(cmin(d[y], d[x] + 1)) 
            q.push(y);
    }
}

void solve() {
    qr(n); m = 0;
    fill_n(id, n, -1);
    {
        int x, y;
        FOR(i, n - 1) {
            qr(x, y);
            x--; y--;
            e[x].pb(y);
            e[y].pb(x);
        }
    }
    VT<int> L;
    rep(i, 0, n - 1) if(SZ(e[i]) == 1) L.pb(i), id[i] = m++; // 叶子节点, 重编号
    for(int x: L) {
        bfs(x);
        rep(i, 0, m - 1) A[id[x]][i] = d[L[i]];
    }
    fill_n(y[0], m, 1.0 / ((n - 1) * m));
    int o = 0;
    FOR(_, 10000) {
        ld sum = accumulate(y[o], y[o] + m, 0.0);
        rep(i, 0, m - 1) {
            ld t = m - 1;
            rep(j, 0, m - 1) t += A[i][j] * y[o][j];
            y[!o][i] = sum / t;
        }
        o ^= 1;
    }
    int s; qr(s); s--;
    ld ans = 0;
    if(id[s] == -1) {
        ans = m - 1;
        bfs(s);
        rep(i, 0, m - 1) ans += y[o][i] * d[L[i]];
        ans /= accumulate(y[o], y[o] + m, (ld)0.0);
    }
    else ans = 1 / y[o][id[s]];
    pr2(ans);
}

技巧四: 独立变量与期望

独立随机变量 x , y x,y x,y, 有: E ( x ⋅ y ) = E ( x ) ⋅ E ( y ) E(x\cdot y)=E(x)\cdot E(y) E(xy)=E(x)E(y).

例题1: 1842G - Tenzing and Random Operations

Given an array a a a of length n n n and an integer v v v.

Tenzing will perform the following operation m m m times:

  1. Choose an integer i i i such that 1 ≤ i ≤ n 1 \leq i \leq n 1in uniformly at random.
  2. For all j j j such that i ≤ j ≤ n i \leq j \leq n ijn, set a j : = a j + v a_j := a_j + v aj:=aj+v.

Tenzing wants to know the expected value of ∏ i = 1 n a i \prod_{i=1}^n a_i i=1nai after performing the m m m operations, modulo 1 0 9 + 7 10^9+7 109+7.

d j , i d_{j,i} dj,i 表示第 j j j 次对 a i a_i ai 的增量(取值 0 or v)
A n s = ∏ i = 1 n ( a i + ∑ j = 1 m d j , i ) 注意到若 j ≠ k , 则 d j , d k 独立 . 若 l < r , E ( d j , l ⋅ d j , r ) = E ( d j , l ) ⋅ v , 即同类 ( same  j ) 只由首项取值决定 Ans=\prod_{i=1}^n (a_i + \sum_{j=1}^m d_{j,i}) \\ 注意到若 j\ne k, 则d_j, d_k 独立. \\ 若l<r, E(d_{j,l}\cdot d_{j,r})=E(d_{j,l}) \cdot v, 即同类(\text {same ~j})只由首项取值决定 \\ Ans=i=1n(ai+j=1mdj,i)注意到若j=k,dj,dk独立.l<r,E(dj,ldj,r)=E(dj,l)v,即同类(same  j)只由首项取值决定
定义$ f[i][j]$ 表示 前$ i$ 项含有$ j 个 ∗ ∗ 不同 ∗ ∗ 类的 个**不同**类的 不同类的d$变量即可, 时间复杂度 O ( n ⋅ min ⁡ ( n , m ) ) O(n\cdot \min(n,m)) O(nmin(n,m)). 空间复杂度 O ( n ) O(n) O(n).

int n, m;
ll f[N], v;

void solve() {
    qr(n, m, v);
    ll x, invn = INV(n, mod);
    f[0] = 1;
    FOR(i, n) {
        qr(x);
        REP(j, 0, min(m, i - 1)) {
            ad(f[j + 1], f[j] * (m - j) % mod * i % mod * invn % mod * v % mod);
            f[j] = (x + j * v) % mod * f[j] % mod;
        }
    }
    ll ans = 0;
    REP(i, 0, min(n, m)) ad(ans, f[i]);
    pr2(ans);
}

交互题

交互题一般的突破口找到某种关键结构, 通过这个关键结构来实现高效求解…

设找到其的概率为 p p p, 则期望进行 1 / p 1/p 1/p 次伯努利实验才能找到.

假设我们进行 t t t 次, 则都失败的概率为 ( 1 − p ) t (1-p)^t (1p)t, 我们希望失败概率极小, 不妨令容错概率为 1 0 − 6 10^{-6} 106, 则 ( 1 − p ) t < 1 0 − 6 → t > − 6 lg ⁡ ( 1 − p ) = − 6 ln ⁡ 10 ln ⁡ ( 1 − p ) ≈ 2.3 × 6 p (1-p)^t < 10^{-6} \rightarrow t >\frac{-6}{\lg (1-p)} = \frac{-6\ln 10}{\ln(1-p)}\approx \frac{2.3 \times 6}p (1p)t<106t>lg(1p)6=ln(1p)6ln10p2.3×6.

简单来说, 进行 == 10 / p 10/p 10/p==次, 出错概率 < 1 0 − 4 <10^{-4} <104.

下面给出 p − t p-t pt 表( t = ⌈ − 6 lg ⁡ ( 1 − p ) ⌉ ) t=\lceil \frac{-6}{\lg (1-p)}\rceil) t=lg(1p)6⌉):

p p p t t t
1/220
1/562
1/10132
1/40546
1/1001375
1/100013809

鸽巢定理例题

n ( 3 ≤ n ≤ 5000 ) n(3\le n\le 5000) n(3n5000) 个隐藏整数 a i ( i ∈ [ 1 , n ] , a i ≤ [ 1 , 4 ] ) a_i(i\in [1, n], a_i\le [1,4]) ai(i[1,n],ai[1,4]), 你需要在 5500次询问内确定整个序列 / 报告这个序列不能唯一确定.

每次询问给出 i , j , k ( i ≠ j , i ≠ k , j ≠ k , i , j , k ∈ [ 1 , n ] ) i,j,k(i\ne j,i\ne k,j\ne k,i,j,k\in [1,n]) i,j,k(i=j,i=k,j=k,i,j,k[1,n]),

  • a i , a j , a k a_i,a_j,a_k ai,aj,ak 可以构成非退化三角形, 则返回 16 s 2 16s^2 16s2, s s s 为三角形面积.
  • 否则, 返回0.

由于返回 16 s 2 = ( a + b + c ) ∗ ( b + c − a ) ∗ ( a + b − c ) ∗ ( a + c − b ) 16s^2=(a+b+c)*(b+c-a)*(a+b-c)*(a+c-b) 16s2=(a+b+c)(b+ca)(a+bc)(a+cb).(海伦公式)

所以我们先打表所有可以唯一确定的三元组.

观察1: (1,4,4), (2,2,3) 具有相同的面积(返回 63). 其他有序三元组的面积都不同.

观察2: (2,2,4) 不合法. 其他不合法三元组最小值是1.

  1. n ≤ 8 n \le 8 n8: 询问所有 ( i , j , k ) (i,j,k) (i,j,k). 然后 4 n 4^n 4n枚举所有状态判断.

  2. n > 8 n > 8 n>8: 由鸽巢定理, 一定存在等边三角形. ( n > 8 = 2 ∗ 4 n>8=2*4 n>8=24)

    1. 如果找到 ( a , a , a ) , a > 1 (a,a,a), a>1 (a,a,a),a>1, 则剩下的所有点都可以一次判断( ( a , a , b ) (a,a,b) (a,a,b)式询问).

    2. 如果找到(1,1,1), 它可以用了分别=1, >1的两种情况.

      > 1 >1 >1的数超过6个时,根据鸽巢原理, 还会存在 ( a , a , a ) ( a > 1 ) (a,a,a)(a>1) (a,a,a)(a>1)的等边三角形. 这时候可以退化为 2.a

      > 1 >1 >1的数不超过6个时, 询问所有相互之间的关系, 然后枚举所有状态判断. O ( 3 k ) O(3^k) O(3k) (值域为 [ 2 , 4 ] [2,4] [2,4] ). 需要在注意应该多增加一个 = 1 =1 =1 的数来辅助(增加一些和1的询问), 因为 1 1 1 可以帮助找出相等的位置 ( 1 , 2 , 2 ) (1,2,2) (1,2,2)

至于怎么找等边三角形:

  • n ≤ 30 n\le 30 n30, 枚举所有三元组.
  • 否则, 随机三元组询问即可, 不难发现当 1~4是均匀分布时, 等边三角形的数量最少, 此时采样成功概率 p = ( 1 / 4 ) 2 = 1 / 16 p=(1/4)^2=1/16 p=(1/4)2=1/16, 故 160次以内高概率找得到.
int n, s[5][5][5], cnt[N];

// #define DE
#ifdef DE
int ans[N];
#endif 

int ask(int i, int j, int k) {
    cout << "? " << i << ' ' << j << ' ' << k << endl;
#ifdef DE
    return s[ans[i]][ans[j]][ans[k]];
#endif 
    qr(i);
    return i;
}

int a[N], msk[N], b[10], val[10], m, v[10][10][10], ok, same[N];

void dfs(int i, int st) {
    if(i == m + 1 || (st == 2 && i == m)) {
        FOR(i, m) rep(j, i + 1, m) rep(k, j + 1, m) 
            if(v[i][j][k] != s[val[i]][val[j]][val[k]]) return;
        if(++ok > 1) {
            cout << "! -1" << endl;
            exit(0);
        }
        FOR(i, m) a[b[i]] = val[i];
        return;
    }
    rep(j, st, 4) val[i] = j, dfs(i + 1, st);
}

void out() {
    cout << "! ";
    FOR(i, n) cout << a[i] << " \n"[i == n];
    cout.flush();
    #ifdef DE
        FOR(i, n) assert(a[i] == ans[i]);
    #endif
}

void vio(int st) {
    FOR(i, m) rep(j, i + 1, m) rep(k, j + 1, m) 
        v[i][j][k] = ask(b[i], b[j], b[k]);
    dfs(1, st);
    assert(ok == 1);
    out();
}

void solve(int x, int y, int z) {
    assert(x > 0 && a[x] > 1);
    int d = a[x];
    FOR(i, 4) same[s[d][d][i]] = i; // guess number
    FOR(i, n) if(!a[i]) a[i] = same[ask(x, y, i)];
    out();
}

void solve() {
    n = 4;
    FOR(i, n) rep(j, i, n) rep(k, j, n) 
        if(i + j > k) {
            s[i][j][k] = s[i][k][j] = s[j][i][k] = s[j][k][i] = s[k][i][j] = s[k][j][i] = 
                (i + j + k) * (i + j - k) * (i + k - j) * (j + k - i);
            cnt[s[i][j][k]]++;
            if(k == i) same[s[i][j][k]] = i;
        }
    qr(n);
    #ifdef DE
        FOR(i, n) qr(ans[i]);
    #endif
    if(n <= 8) {
        FOR(i, n) b[++m] = i;
        vio(1);
    }
    else {
        int x = -1, y, z;
        if(n <= 30) {
            FOR(i, n) rep(j, i + 1, n) rep(k, j + 1, n) {
                int t = ask(i, j, k);
                if(t = same[t]) {
                    a[i] = a[j] = a[k] = t;
                    x = i; y = j; z = k;
                    goto nxt;
                }
            }
        }
        else {
            mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
            auto rd = uniform_int_distribution<int>(1, n);
            while(1) {
                VT<int> v{rd(rng), rd(rng), rd(rng)};
                sort(v);
                if(v[0] == v[1] || v[1] == v[2]) continue;
                int t = ask(v[0], v[1], v[2]);
                if(t = same[t]) {
                    x = v[0]; y = v[1]; z = v[2];
                    a[x] = a[y] = a[z] = t;
                    goto nxt;
                }
            }
        }
        nxt:
        assert(x != -1);
        if(a[x] == 1) {
            FOR(i, n) if(!a[i]) {
                int t = ask(x, y, i);
                if(t) a[i] = 1;
                else {
                    b[++m] = i;
                    if(m == 7) {
                        FOR(u, m) rep(v, u + 1, m) rep(w, v + 1, m) {
                            t = ask(b[u], b[v], b[w]);
                            if(t = same[t]) {
                                a[b[u]] = a[b[v]] = a[b[w]] = t;
                                return solve(b[u], b[v], b[w]);
                            }
                        }
                    }
                }
            }
            b[++m] = x; val[m] = 1;
            vio(2);
        }
        else solve(x, y, z);
    }
}

图与生成树

树是最简单的连通图, 给图的交互题, 可以试着先从生成树入手, 找到生成树后, 剩余的结构可能很好解决.

1819E - Roads in E City

参考博客题解 CF1819E 【Roads in E City】 - 洛谷专栏

主要思想:

由于已维修的道路构成连通块, 所以

  • 如果 ( u , v ) (u,v) (u,v) 不是桥, 则不会影响其他点的连通性;

  • 如果边 ( u , v ) (u,v) (u,v) 是桥, 那么断开后, 我们询问 k k k u u u, k k k v v v.

    u u u 侧连通块大小为 t t t, 则 2 k 2k 2k 次询问均没有0的概率(假设题目的 s ∼ U [ 1 , n ] s\sim U[1,n] sU[1,n]) 为 ( t n ⋅ n − t n ) k ≤ 1 4 k \left(\dfrac {t}n \cdot \dfrac {n -t}n \right)^k\le \dfrac 1 {4^k} (ntnnt)k4k1.

如何找生成树? 只需要把不是桥的边删去即可, 最后一定会剩余 n − 1 n-1 n1 条桥边. 这里次数为 O ( 2 k m ) O(2k m) O(2km)

剩余的边 ( s , t ) (s,t) (s,t), 我们只需删掉 s , t s,t s,t 生成树路径上的一条边, 再加入 ( s , t ) (s,t) (s,t) s , t s,t s,t 是否连通即可. 这个次数也是 O ( 2 k m ) O(2km) O(2km).

总共大概需要 4 k m 4km 4km 次, 我们令 k = 20 k=20 k=20 即可.


const int N = 2010, K = 20;
int n, m, cut[N], pre[N], dep[N];
VT<pii> e[N]; 

int ask(int x) {
    assert(1 <= x && x <= n);
    cout << "? " << x << endl;
    qr(x); return x;
}

void dfs(int x) {
    for(auto [y, w]: e[x]) if(!dep[y]) {
        dep[y] = dep[x] + 1;
        pre[y] = w; // Edge_id
        dfs(y);
    }
}

void solve() {
    qr(n, m);
    FOR(i, n) e[i].clear();
    int x, y;
    VT<pii> E;
    FOR(i, m) {
        qr(x, y);
        E.emplace_back(x, y);
    }
    auto check = [&](int i, bool ban) ->int { // bridge
        if(ban) cout << "- " << i + 1 << endl; // ban
        auto [x, y] = E[i];
        FOR(i, K) {
            if(!ask(x)) return 1;
            if(!ask(y)) return 1;
        }
        return 0;
    };
    rep(i, 0, m - 1) {
        if(cut[i] = check(i, 1)) {
            cout << "+ " << i + 1 << endl; // recover
            auto [x, y] = E[i];
            e[x].pb({y, i + 1}), 
            e[y].pb({x, i + 1});
        }
    }
    fill_n(dep + 1, n, 0);
    dep[1] = 1; dfs(1);
    rep(i, 0, m - 1) {
        if(!cut[i]) {
            auto [x, y] = E[i];
            if(dep[x] < dep[y]) swap(x, y);
            cout << "- " << pre[x] << endl;
            cout << "+ " << i + 1 << endl;
            cut[i] = !check(i, 0);
            cout << "- " << i + 1 << endl;
            cout << "+ " << pre[x] << endl;
        }
    }
    cout << "! ";
    rep(i, 0, m - 1) cout << cut[i] << " \n"[i == m - 1];
    cout.flush(); qr(n); if(!n) exit(0);
}

简单DP

这类DP无需考虑后效性, 直接设计状态即可.

  1. Problem - 1835E - Codeforces: f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 表示已知 i i i 个有效键位, j j j 个无效键位, { k = 0 : 已找到删除键 ; k = 1 : 正在寻找删除键 ; k = 2 : 已找到删除键 . \begin{cases} k=0:已找到删除键; \\k=1:正在寻找删除键;\\k=2: 已找到删除键. \end{cases} k=0:已找到删除键;k=1:正在寻找删除键;k=2:已找到删除键. 的状态的概率.在需要回撤时直接 p ∗ c o s t p*cost pcost 即可计算期望开销.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Infinite_Jerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值