正整数随机变量 x x x 的期望值: E ( x ) = ∑ i = 1 ∞ P ( x > = i ) E(x)=\sum_{i=1}^{\infty} P(x>=i) E(x)=∑i=1∞P(x>=i)
Blocks
期望dp,从已经满足的点倒着推,首先考虑状态,发现
n
n
n很小,直接状压,然后暴力枚举状态看是否全部覆盖,发现坐标跨度很大,对坐标离散化,依次差分修改,
O
(
n
2
2
n
)
O(n^22^n)
O(n22n),然后就可以直接dp了
d
p
i
=
∑
j
d
p
i
[
(
1
<
<
j
)
&
i
]
+
1
+
∑
j
d
p
i
∣
j
+
1
[
(
1
<
<
j
)
&
1
=
=
0
]
n
dp_i={{\sum_j dp_i [(1<<j)\&i]+1+ \sum_j dp_{i|j}+1 [(1<<j)\&1==0] }\over n}
dpi=n∑jdpi[(1<<j)&i]+1+∑jdpi∣j+1[(1<<j)&1==0]
化简得:
d
p
i
=
n
+
∑
j
d
p
i
∣
j
[
(
1
<
<
j
)
&
1
=
=
0
]
n
−
c
n
t
1
(
i
)
dp_i={ n+\sum_j dp_{i|j} [(1<<j)\&1==0] \over n-cnt1(i)}
dpi=n−cnt1(i)n+∑jdpi∣j[(1<<j)&1==0]
P3239 [HNOI2015] 亚瑟王
一张卡牌一整局只会产生一次贡献,不妨直接考求出这一次贡献所产生的的概率为多少。
设 g i g_i gi表示第 i i i张牌在 r r r轮中发动一次技能的概率,发现这个东西不太好找前后的递推关系。我们发现当前卡牌是否发动技能的概率受到前面卡牌的影响(前面选了几张卡,那么我再怎么也要有几轮把前面的卡选完),但不受到后面卡牌的影响,所以我们设 f i , j f_{i,j} fi,j,表示在这 r r r轮中,前 i i i张牌有 j j j张牌被选的概率,这样我们就知道有多少轮能在 i i i这个地方做出决策(产生被选的概率),从而算出 g i g_i gi
然后 f i , j f_{i,j} fi,j每次只要看在 r r r轮中 i i i这张卡牌是否产生发动一次技能,分类转移
std::vector<std::vector<double>> f(n+1,std::vector<double>(r+1));
f[0][0]=1;
for (int i=1;i<=n;i++){
for (int j=0;j<=r;j++){
f[i][j]=f[i-1][j]*ksm(1-p[i],r-j);
if (j) f[i][j]+=f[i-1][j-1]*(1-ksm(1-p[i],r-j+1));
}
}
std::vector<double> g(n+1);
double ans=0;
for (int i=1;i<=n;i++){
for (int j=0;j<=r;j++){
g[i]+=f[i-1][j]*(1-ksm(1-p[i],r-j));
}
ans+=g[i]*d[i];
}
std::cout<<std::fixed<<std::setprecision(10)<<ans<<"\n";
P3750 [六省联考 2017] 分手是祝愿
50%的数据
k
=
=
n
k==n
k==n,也就是说只要找到最小的关灯次数
直接从后往前遍历,当前点为
i
i
i,如果现在
i
i
i这个位置为1,那只能在这按一次开关,然后
O
(
n
)
O(\sqrt n)
O(n)去修改其它位置的状态。
考虑有等概率随机按开关的情况,其实在前面我们就能知道,给出的数据我们一共要按的开关就只有那几个,一个开关重复按两次是没有效果的,按错了开关到最后也得按回去,所以我们状态设计就是已经按了几个要按的开关,
f
i
f_i
fi表示已经按了
i
i
i个要按的开关的期望。
i
<
=
k
,
f
i
=
k
i<=k,f_i=k
i<=k,fi=k
i
>
k
i>k
i>k 我们分类按对了开关或按错了开关:
f
i
=
i
n
⋅
f
i
−
1
+
n
−
i
n
⋅
f
i
+
1
f_i={i \over n}\cdot f_{i-1}+{n-i \over n} \cdot f_{i+1}
fi=ni⋅fi−1+nn−i⋅fi+1
令
f
i
=
f
i
−
1
+
b
i
f_i=f_{i-1}+b_i
fi=fi−1+bi
b
i
b_i
bi代入式子中解出
b
i
b_i
bi的递推式,然后根据推出
f
i
f_i
fi
另一种状态设计就是
f
i
f_i
fi表示剩下
i
i
i个开关要按,到剩下
i
−
1
i-1
i−1个开关要按的期望为多少
f
i
=
i
n
+
n
−
i
n
(
1
+
f
i
+
1
+
f
i
)
f_i={i \over n}+{n-i \over n}(1+f_{i+1}+f_{i})
fi=ni+nn−i(1+fi+1+fi) 然后移项即可得到递推式
最后累加
代码是第一种方法
std::vector<ll> b(n+1);
b[n]=1;
int cnt=0;
for (int i=n;i>=1;i--){
if (a[i]){
cnt++;
for (int j=1;j*j<=i;j++){
if (i%j==0){
a[j]^=1;
if (i/j!=j) a[i/j]^=1;
}
}
}
if (i<n){
b[i]=(n-i+mod)%mod*b[i+1]%mod*ni(i)%mod+n*ni(i)%mod;
b[i]%=mod;
}
}
std::vector<ll> f(n+1);
for (int i=1;i<=k;i++){
f[i]=i%mod;
}
for (int i=k+1;i<=cnt;i++){
f[i]=(f[i-1]+b[i])%mod;
}
ll ans=f[cnt];
for (int i=1;i<=n;i++){
ans=ans*i%mod;
}
std::cout<<ans;
P3412 仓鼠找sugar II
首先考虑固定终点,令终点为根节点,
f
i
f_i
fi表示从
i
i
i到其父亲节点的期望步数,对于叶子结点
f
i
=
1
f_i=1
fi=1,然后推式子:
f
i
=
1
d
e
g
i
+
∑
s
o
n
1
+
f
s
o
n
+
f
i
d
e
g
i
f_i={1 \over deg_i}+{\sum_{son} 1+f_{son}+f_i \over deg_i}
fi=degi1+degi∑son1+fson+fi
f
i
=
d
e
g
i
+
∑
s
o
n
f
s
o
n
f_i=deg_i+\sum_{son} f_{son}
fi=degi+son∑fson
然后计算不同起点每个
f
i
f_i
fi的贡献
f
i
⋅
s
z
i
f_i \cdot sz_i
fi⋅szi
然后换根求不同终点的期望
P3232 [HNOI2013] 游走
这道跟上一道的区别是这道变成了图,上一道因为可以根据dfs序直接推过去,但这一道可能会形成多元环,所以用高斯消元
计算每个点经过次数的期望
f
i
f_i
fi,那么对于一条边其经过的期望次数为
f
u
d
e
g
u
+
f
v
d
e
g
v
{f_u \over deg_u} + {f_v \over deg_v}
degufu+degvfv,然后贪心求贡献
f i f_i fi式子很简单,注意特判一下 i = = 1 i==1 i==1
P3830 [SHOI2012] 随机树
第一个询问:
f
i
f_i
fi表示叶子个数为
i
i
i个点的期望
f
i
=
(
i
−
1
)
∗
f
i
−
1
+
f
i
−
1
+
2
i
f_i={(i-1)*f_{i-1}+f_{i-1}+2 \over i}
fi=i(i−1)∗fi−1+fi−1+2化简得
f
i
=
f
i
−
1
+
2
i
f_i=f_{i-1}+{2 \over i}
fi=fi−1+i2
第二个询问
E
(
x
)
=
∑
i
=
1
∞
P
(
x
>
=
i
)
E(x)=\sum_{i=1}^{\infty} P(x>=i)
E(x)=∑i=1∞P(x>=i)
那么设
f
i
,
j
f_{i,j}
fi,j ,表示叶子个数为
i
i
i,深度大于等于
j
j
j的概率,那么根据公式就很好求期望了
发现不好转移,因为不知道每种情况的出现的概率,转移肯定是由左右子树叶子数转移,所以去想一个树,左右子树叶子数确定的情况下,出现的概率为多少:比如已知总叶子为 i i i,左子树叶子有 k k k个,那么右子树叶子有 i − k i-k i−k个,那么这样构造的方案数:首先只考虑每次选左或右方案数 C i − 1 k = ( i − 1 ) ! k ! ( i − k − 1 ) ! C_{i-1}^{k}={(i-1)! \over k!(i-k-1)!} Ci−1k=k!(i−k−1)!(i−1)!,然后考虑左右子树的形态 k ! ( i − k − 1 ) ! k!(i-k-1)! k!(i−k−1)!,最终得 ( i − 1 ) ! (i-1)! (i−1)!,发现和左右子树叶子数无关,那么 i i i个叶子的树种,左子树有 k k k个叶子的概率为 1 i − 1 1 \over i-1 i−11
f i , j = f k , j − 1 + f i − k , j − 1 − f k , j − 1 ⋅ f i − k , j − 1 i − 1 f_{i,j}={f_{k,j-1}+f_{i-k,j-1}-f_{k,j-1} \cdot f_{i-k,j-1}\over i-1} fi,j=i−1fk,j−1+fi−k,j−1−fk,j−1⋅fi−k,j−1
P4206 [NOI2005] 聪聪与可可
易证猫跟老鼠的距离一定是递减的,所以不会有后效性,状态设为
i
,
j
i,j
i,j表示猫和老鼠的位置
猫运动可以预处理,然后按照题意记忆化搜索即可
CF 908D New Year and Arbitrary Arrangement
倒推
f
i
,
j
f_{i,j}
fi,j表示前缀中有
i
i
i个
a
a
a,已有的
a
b
ab
ab对有
j
j
j组的期望对数
f
i
,
j
f_{i,j}
fi,j由
f
i
+
1
,
j
,
f
i
,
i
+
j
f_{i+1,j},f_{i,i+j}
fi+1,j,fi,i+j转移 特判一下边界
i
+
j
>
=
k
i+j>=k
i+j>=k时所需要的期望
推式子:
p
b
∑
a
=
0
∞
(
i
+
j
+
a
)
p
a
a
p_b\sum_{a=0}^{\infty} (i+j+a)p_a^a
pba=0∑∞(i+j+a)paa
p
b
(
(
i
+
j
)
∑
a
=
0
∞
p
a
+
∑
a
=
0
∞
a
p
a
a
)
=
p
b
(
i
+
j
1
−
p
a
+
p
a
∑
a
=
0
∞
a
p
a
a
−
1
)
p_b((i+j)\sum_{a=0}^{\infty} p_a+ \sum_{a=0}^{\infty}ap_a^a)=p_b({i+j \over 1-p_a}+ p_a \sum_{a=0}^{\infty}ap_a^{a-1})
pb((i+j)a=0∑∞pa+a=0∑∞apaa)=pb(1−pai+j+paa=0∑∞apaa−1)
i
+
j
+
p
b
p
a
(
∑
a
=
0
∞
p
a
a
)
′
=
i
+
j
+
p
b
p
a
(
1
1
−
p
a
)
′
i+j +p_b p_a (\sum_{a=0}^{\infty}p_a^a)'=i+j +p_b p_a ({1 \over 1-p_a})'
i+j+pbpa(a=0∑∞paa)′=i+j+pbpa(1−pa1)′
i
+
j
+
p
b
p
a
(
1
−
p
a
)
2
=
i
+
j
+
p
b
p
a
p
b
2
=
i
+
j
+
p
a
p
b
i+j +p_b {p_a \over (1-p_a)^2}=i+j+p_b{p_a \over p_b^2}=i+j+{p_a \over p_b}
i+j+pb(1−pa)2pa=i+j+pbpb2pa=i+j+pbpa
CF 398B Painting The Wall
找规律,状态为已经有几行几列合法,然后推出对所有点分类:
无贡献,贡献行,贡献列,贡献行列
CF 280C Game on Tree
考虑每一个点被选择的概率,如果它有一个祖先先选,该点不产生贡献;如果它在所有祖先前先选,产生
1
s
u
m
祖先
1 \over sum_{祖先}
sum祖先1的贡献,
s
u
m
祖先
=
d
e
p
sum_{祖先}=dep
sum祖先=dep
CF 248E Piglet’s Birthday
发现
i
i
i货架没吃过的罐子<=
a
i
a_i
ai ,设
f
i
,
j
f_{i,j}
fi,j表示第
i
i
i个货架,有
j
j
j个罐子没吃过的概率,然后可以模拟题目过程求出当前货架罐子数,每次贡献只会从移出罐子的货架产生,所以枚举每次转移中已经吃过的罐子数,组合数求概率
CF 123E Maze
固定起点为根节点
s
s
s,发现很难直接dp,考虑此题的一些特殊性质
假设现在要到
t
t
t,仅存在一条路径,假设当前点为
u
u
u,对其儿子随机排序,令当前正确走向的儿子为
v
v
v,则其它儿子有两种选择,一种排在
v
v
v前,另一种排在
v
v
v后,一旦排在
v
v
v前,会产生
2
⋅
s
z
2 \cdot sz
2⋅sz的贡献,期望贡献为
s
z
sz
sz ;排在
v
v
v后无贡献。
转换为,去掉
t
t
t子树后,树的大小为
s
s
s到
t
t
t的期望步数。
那么跟节点固定后,只需要维护去掉子树的期望大小
f
f
f,最后根节点
s
s
s的贡献为
(
n
−
f
)
∗
s
p
s
(n-f)*sp_s
(n−f)∗sps;
后续只需要通过换根dp,就可以求出各点作为根节点的贡献
CF103687 E
首先分类:
T
1
>
=
T
2
/
S
=
=
0
T1>=T2/S==0
T1>=T2/S==0,说明只能用第二种回血方式,贪心的想,只要每次在还剩两滴血并且
(
1
−
p
i
)
(1-p_i)
(1−pi)的概率下花费
T
2
T2
T2时间即可
T 1 < T 2 T1<T2 T1<T2,首先还是能不回血就不回血,然后有法力优先用 T 1 T1 T1,没法力用 T 2 T2 T2。但是如果当前点是无限回蓝点,转移时可以直接从 f i + 1 , j , S f_{i+1,j,S} fi+1,j,S转移到 f i , j , S f_{i,j,S} fi,j,S(法力越高,期望肯定越低)。然后为了更好转移到 i − 1 i-1 i−1,我们要更新一下不是满法力的状态(有个细节就是要注意转移的顺序,详见代码)
#include <bits/stdc++.h>
#define ll long long
double f[1005][15][10],p[1005];
bool vis[1005];
void yrzr(){
int n,H,S;
std::cin>>n>>H>>S;
for (int i=0;i<n;i++){
std::cin>>p[i];
p[i]/=100;
}
int K;
std::cin>>K;
for (int i=1;i<=K;i++){
int x;
std::cin>>x;
vis[x-1]=1;
}
double T1,T2;
std::cin>>T1>>T2;
for (int i=0;i<n;i++){
for (int j=0;j<=H;j++){
for (int k=0;k<=S;k++){
f[i][j][k]=1e15;
}
}
}
if (T1>=T2||S==0){
for (int i=n-1;i>=0;i--){
f[i][2][0]=(1+p[i]*f[i+1][2][0]+(1-p[i])*T2)/p[i];
for (int j=3;j<=H;j++){
f[i][j][0]=1+p[i]*f[i+1][j][0]+(1-p[i])*f[i][j-1][0];
}
}
std::cout<<std::fixed<<std::setprecision(7)<<f[0][H][0];
}else{
for (int i=n-1;i>=0;i--){
if (!vis[i]){
f[i][2][0]=(1+p[i]*f[i+1][2][0]+(1-p[i])*T2)/p[i];
for (int k=1;k<=S;k++){
f[i][2][k]=1+p[i]*f[i+1][2][k]+(1-p[i])*(f[i][2][k-1]+T1);
for (int j=3;j<=H;j++){
f[i][j][k]=1+p[i]*f[i+1][j][k]+(1-p[i])*f[i][j-1][k];
}
}
}else{
for (int j=2;j<=H;j++){
if (j==2){
f[i][2][S]=(1+p[i]*f[i+1][2][S]+(1-p[i])*T1)/p[i];
}else{
f[i][j][S]=1+p[i]*f[i+1][j][S]+(1-p[i])*f[i][j-1][S];
}
for (int k=j;k<=H;k++){
// f[i][j][S]=std::min(f[i][j][S],(k-j)*T1+f[i][k][S]);
//转移时不能直接这么转移,因为此时f[i][k][S]还没更新,所以我们可以直接手动化成f[i+1]的式子
f[i][j][S]=std::min(f[i][j][S],(k-j)*T1+(1+p[i]*f[i+1][k][S]+(1-p[i])*T1)/p[i]);
}
for (int k=0;k<=S;k++){
f[i][j][k]=std::min(f[i][j][k],f[i][j][S]);
}
}
}
}
std::cout<<std::fixed<<std::setprecision(7)<<f[0][H][S];
}
}