据说这道题JAG2015赛场上只有一个队AC。。。
题解:
设 p p p为一次赌博获胜的概率, q q q为 1 − p 1-p 1−p,即输的概率。
仔细思考一下发现实际上就是要我们对于 i ∈ [ 1 , m − 1 ] i\in [1,m-1] i∈[1,m−1]求一个决策表 k i k_i ki,表示当前有 i i i枚硬币的时候我们会赌 k i k_i ki枚硬币,显然这是一个马尔科夫过程,当前状态与之前的状态和决策无关。设 p i p_i pi表示当前有 i i i枚硬币,在将硬币输光之前能够赢到 m m m枚的概率,根据定义显然有:
p 0 = 0 p i = 1 , ∀ i ≥ m p i = p ⋅ p i + k i + q ⋅ p i − k i , ∀ i ∈ [ 1 , m − 1 ] \begin{aligned} p_0&=0 \\p_i&=1,\forall i\geq m\\ p_i&=p\cdot p_{i+k_i}+q\cdot p_{i-k_i} ,\forall i\in [1,m-1] \end{aligned} p0pipi=0=1,∀i≥m=p⋅pi+ki+q⋅pi−ki,∀i∈[1,m−1]
显然 p i p_i pi是一个递增数列, k i ≤ min ( i , m − i ) k_i\leq \min(i,m-i) ki≤min(i,m−i)。
现在求不出 k i k_i ki,所以我们可以把限制写成:
p i = max 1 ≤ j ≤ min ( i , m − i ) { p ⋅ p i + j + q ⋅ p i − j } p_i=\max_{1\leq j\leq \min(i,m-i)}\{p\cdot p_{i+j}+ q\cdot p_{i-j}\} pi=1≤j≤min(i,m−i)max{p⋅pi+j+q⋅pi−j}
直接按照这个式子暴力迭代并特判 p = 0 p=0 p=0和 p = 1 p=1 p=1的情况可以得到35pts的好成绩。
接下来考虑对于特殊数据进行特殊处理:
1 . p = 0.5
首先明确一点,合法的 p i p_i pi序列唯一。
那么我们试图找一个序列 p i p_i pi,使得在 p = 0.5 p=0.5 p=0.5的时候满足上面的式子。
显然 p i = i m p_i=\frac{i}{m} pi=mi即可。此时每个 p i p_i pi都是 p i + j p_{i+j} pi+j和 p i − j p_{i-j} pi−j的平均值,则 j ∈ [ 1 , min ( i , m − i ) ] j\in[1,\min(i,m-i)] j∈[1,min(i,m−i)]的所有决策都是可行决策。
2 . p > 0.5
此时直觉告诉我们每次赌一枚硬币是最优的。。。
不要问我为什么,问就是打表找规律,而且日语官方题解也就写的是凭直觉。
如果要证明的话,日语官方题解是这样说的:
1 ドルずつ賭けるとして勝率を求め,
さっきの ?? の不等式を満たすことを示す
假设每次赌一枚硬币,求概率,满足前面的所有限制。
那么我们把概率求出来,发现是这样一个式子: p i = p ⋅ p i + 1 + q ⋅ p i − 1 p_i=p\cdot p_{i+1}+q\cdot p_{i-1} pi=p⋅pi+1+q⋅pi−1,也就是 p ⋅ p i + 1 = p i − q ⋅ p i − 1 p\cdot p_{i+1}=p_i-q\cdot p_{i-1} p⋅pi+1=pi−q⋅pi−1
利用特征方程可以把这个解出来,设 r = q p r=\frac{q}{p} r=pq,解出来是 p i = 1 − r i 1 − r m p_i=\frac{1-r^i}{1-r^m} pi=1−rm1−ri,这个形式就可以直接证明上述大于等于号成立了。
3 . p < 0.5
数学公式警告,长篇大论警告
这部分是这道题真正扯淡的地方。。。
也是这道题真正需要扯概率论的地方,为了保证你能够看懂下面的讲解,请补习鞅论(martingale)和停时理论(Optional Stopping Theorem)。其实不补也行,下面的可以感性理解。
设 p c ( i ) pc(i) pc(i)表示 i i i在表示成二进制数之后, 1 1 1的个数
首先看一个问题,现在你有 x x x枚硬币,每次可以赌任意数量的硬币吗,赔率 1 : 1 1 : 1 1:1,赢的概率为 p < 0.5 p < 0.5 p<0.5,你见好就收的上界为 2 k 2^k 2k,问你在输光之前能否见好就收。
考虑这样一个策略,每次赌 2 l 2^l 2l,其中 2 l 2^l 2l是最大的能够整除 x x x的 2 2 2的整数次幂,也就是所谓的lowbit。
这个策略下初始硬币数为 x x x的胜率为: f ( x ) = ∑ i = 0 x − 1 q p c ( i ) p k − p c ( i ) f(x)=\sum\limits_{i=0}^{x-1}q^{pc(i)}p^{k-pc(i)} f(x)=i=0∑x−1qpc(i)pk−pc(i)
或者说,设 x x x的二进制拆分为 x = ∑ i = 1 k b i 2 k − i x=\sum\limits_{i=1}^{k}b_i2^{k-i} x=i=1∑kbi2k−i,则 f ( x ) = ∑ i = 1 k b i p i − ∑ j = 1 i − 1 b j q ∑ j = 1 i − 1 b j f(x)=\sum\limits_{i=1}^{k}b_ip^{i-\sum\limits_{j=1}^{i-1}b_j}q^{\sum\limits_{j=1}^{i-1} b_j} f(x)=i=1∑kbipi−j=1∑i−1bjqj=1∑i−1bj,然后由这个式子可以导出上面那个式子。
证明随便归纳一下就行了。
可以证明
f
(
x
)
f(x)
f(x)是所有策略中的上鞅,考虑赌
k
k
k枚硬币,上鞅的意思就是说下面这个式子始终成立,我尽量解释得通俗了:
f
(
x
)
≥
f
(
x
+
k
)
⋅
p
+
f
(
x
−
k
)
⋅
q
f(x)\geq f(x+k)\cdot p+f(x-k)\cdot q
f(x)≥f(x+k)⋅p+f(x−k)⋅q
重写上面的命题,也就是 q ⋅ ( f ( x ) − f ( x − k ) ) ≥ p ⋅ ( f ( x + k ) − f ( x ) ) ⟺ ∑ i = x − k x − 1 q p c ( i ) + 1 p k − p c ( i ) ≥ ∑ i = x x + k − 1 q p c ( i ) p k − p c ( i ) + 1 q\cdot (f(x)-f(x-k))\geq p\cdot (f(x+k)-f(x))\\ \Longleftrightarrow\sum_{i=x-k}^{x-1}q^{pc(i)+1}p^{k-pc(i)}\geq \sum_{i=x}^{x+k-1}q^{pc(i)}p^{k-pc(i)+1} q⋅(f(x)−f(x−k))≥p⋅(f(x+k)−f(x))⟺i=x−k∑x−1qpc(i)+1pk−pc(i)≥i=x∑x+k−1qpc(i)pk−pc(i)+1
考虑在两个集合中构建双向映射来证明上面的式子, { x − k , ⋯ x − 1 } ⇔ { x , ⋯ , x + k − 1 } \{x-k,\cdots x-1\}\Leftrightarrow\{x,\cdots,x+k-1\} {x−k,⋯x−1}⇔{x,⋯,x+k−1},使得 ∀ a ∈ [ x − k , x − 1 ] , ∃ b ∈ [ x , x + k − 1 ] \forall a\in[x-k,x-1],\exist b\in[x,x+k-1] ∀a∈[x−k,x−1],∃b∈[x,x+k−1],有 p c ( a ) + 1 ≥ p c ( b ) pc(a)+1\geq pc(b) pc(a)+1≥pc(b),则 q p c ( a ) + 1 p k − p c ( i ) ≥ q p c ( b ) p k − p c ( i ) + 1 q^{pc(a)+1}p^{k-pc(i)}\geq q^{pc(b)}p^{k-pc(i)+1} qpc(a)+1pk−pc(i)≥qpc(b)pk−pc(i)+1,全部加起来即可证明上式。
怎么构造上面那个双向映射,找到最小的
s
s
s,满足
s
≥
k
s\geq k
s≥k且
s
s
s是
2
2
2的整数次幂。然后将
x
−
k
x-k
x−k与
x
−
k
+
s
x-k+s
x−k+s配对,
x
−
k
+
1
x-k+1
x−k+1与
x
−
k
+
s
+
1
x-k+s+1
x−k+s+1配对,以此类推,注意到
s
≤
2
k
s\leq 2k
s≤2k所以我们能够至少成功匹配一对,将
k
k
k设置成
s
−
k
s-k
s−k继续配对,显然能够构成一个完备匹配。
显然由于只在一个二进制位上
+
1
+1
+1,这个构造能够满足上面的限制,如果有进位更好,二进制位的
1
1
1只会变少不会变多。
f ( x ) f(x) f(x)是所有策略的上鞅得证。
由停时理论可以知道 f ( x ) f(x) f(x)是最优策略。
显然我们可以将 x x x变为 x 2 k \frac{x}{2^k} 2kx,将 2 k 2^k 2k变为 1 1 1做同等意义下的赌博,设此时答案为 g ( x ) g(x) g(x)。
也就是说我们能够直接处理 2 2 2进制有限小数的情况,将 x x x写成二进制小数的形式: 0. b 1 b 2 ⋯ b k 0.b_1b_2\cdots b_k 0.b1b2⋯bk,结论和上面的相同。
如果
x
x
x是二进制无限小数,设
x
k
x_k
xk表示
x
x
x截断前
k
k
k位得到的有限小数,我们直接将
x
k
x_k
xk的策略用到
x
x
x上不会差,因为
x
>
x
k
x>x_k
x>xk。我们可以这样将
k
k
k向无穷大靠近来逼近真实值,由于这个乘积是收敛的,所以可以直接算,在当前乘积小于精度的时候break掉。(其实如果
m
m
m再小一点我们可以直接开一个vis数组求循环节,由于几何级数收敛能够求出精确值,于是这道题可以扔到取模的环境下改成毒瘤题了)这种情况下的
g
(
x
)
=
∑
i
=
1
∞
b
i
p
i
−
∑
j
=
1
i
−
1
b
j
q
∑
j
=
1
i
−
1
b
j
g(x)=\sum\limits_{i=1}^\infty b_ip^{i-\sum\limits_{j=1}^{i-1}b_j}q^{\sum\limits_{j=1}^{i-1} b_j}
g(x)=i=1∑∞bipi−j=1∑i−1bjqj=1∑i−1bj
f ( x ) f(x) f(x)是针对离散游戏, g ( x ) g(x) g(x)很显然是针对连续游戏。
g ( x ) g(x) g(x)是这个连续游戏的上鞅,可以根据 f ( x ) f(x) f(x)是离散游戏的上鞅加上调整法来证明。由停时理论,同样 g ( x ) g(x) g(x)是最优决策下的获胜概率。
算答案的话直接将 x = n m x=\frac{n}{m} x=mn的二进制表示倒出来一下,然后用 g ( x ) g(x) g(x)来算就行了,注意最坏情况下循环节长度是 O ( ϕ ( m ) ) O(\phi(m)) O(ϕ(m))的,无法承受,在精度爆炸的时候直接break掉就行了。
现在问题就只剩下哪些是 x = n x=n x=n的时候第一步的可能最优决策。
现在上面那个关于 f f f的取最小的2的整数次幂的结论用不了了,因为 n m \frac{n}{m} mn可能是二进制无限循环小数。
考虑一个贪心策略,每次赌 min ( n , m − n ) \min(n,m-n) min(n,m−n),这个显然是一个最优策略,证明可以考虑调整法。设 r = q p , r > 1 r=\frac{q}{p},r > 1 r=pq,r>1。关于 g ( x ) g(x) g(x),我们有如下的关系式:
g ( x ) = p + q g ( 2 x − 1 ) , x ≥ 1 2 g ( x ) = p ⋅ g ( 2 x ) , x < 1 2 \begin{aligned} g(x)&=p+qg(2x-1),x\geq \frac{1}{2}\\ g(x)&=p\cdot g(2x),x < \frac{1}{2}\\ \end{aligned} g(x)g(x)=p+qg(2x−1),x≥21=p⋅g(2x),x<21
设 r = q p , x = ∑ i 2 b i r=\frac{q}{p},x=\sum\limits_{i}2^{b_i} r=pq,x=i∑2bi, b i b_i bi允许为负数,上面的一大堆式子可以改写成:
g ( x ) = ∑ i r i ( 1 + r ) b i ( r + 1 ) g ( x ) = 1 + r g ( 2 x − 1 ) , x ≥ 1 2 ( r + 1 ) g ( x ) = g ( 2 x ) , x < 1 2 \begin{aligned} g(x)&=\sum_{i}r^i(1+r)^{b_i}\\ (r+1)g(x)&=1+rg(2x-1),x\geq \frac{1}{2}\\ (r+1)g(x)&=g(2x),x < \frac{1}{2} \end{aligned} g(x)(r+1)g(x)(r+1)g(x)=i∑ri(1+r)bi=1+rg(2x−1),x≥21=g(2x),x<21
我来人工翻译一下官方题解接下来的的叙述,各大翻译网站对于学术语句的日翻中都难以直视,事实证明会一点日语还是有用的,多亏了平时看番。
接下来的推导看到 f f f请自动认为是 g g g,官方题解用的 f f f,我用的 g g g,翻译的时候难免会有纰漏。
考虑证明 g ( x + y ) ≥ g ( x ) + r ⋅ g ( y ) , ( x ≥ y ≥ 0 ) g(x+y)\geq g(x)+r\cdot g(y),(x\geq y\geq 0) g(x+y)≥g(x)+r⋅g(y),(x≥y≥0)
设
x
x
x最高位为
2
e
0
2^{e_0}
2e0,
x
=
2
e
0
+
x
′
x=2^{e_0}+x'
x=2e0+x′,则
g
(
x
)
=
(
r
+
1
)
e
0
+
r
g
(
x
′
)
g(x)=(r+1)^{e_0}+rg(x')
g(x)=(r+1)e0+rg(x′),接下来对
y
y
y的值分类讨论,看掉一个“ない”把我懵逼了半天:
-
y
y
y的最高位也是
2
e
0
2^{e_0}
2e0的情况,
设 y = 2 e 0 + y ′ y=2^{e_0}+y' y=2e0+y′,则 g ( x + y ) − g ( x ) − r ⋅ g ( y ) = r ( g ( x ′ + y ′ ) − g ( x ′ ) − r ⋅ g ( y ′ ) ) g(x+y)-g(x)-r\cdot g(y)=r(g(x'+y')-g(x')-r\cdot g(y')) g(x+y)−g(x)−r⋅g(y)=r(g(x′+y′)−g(x′)−r⋅g(y′)),显然 y ′ ≤ x ′ y'\leq x' y′≤x′,归纳证明即可。 -
y
y
y的最高位低于
2
e
0
2^{e_0}
2e0,且
x
,
y
x,y
x,y相加进位到了
2
e
0
+
1
2^{e_0+1}
2e0+1。
以下两式成立:
g ( x + y ) − g ( x ) − r ⋅ g ( y ) = ( r − 1 ) ( ( r + 1 ) e 0 − g ( x ′ ) ) + g ( x ′ + y ) − g ( x ′ ) − r g ( y ) g ( x + y ) − g ( x ) − r ⋅ g ( y ) = ( r − 1 ) ( ( r + 1 ) e 0 − g ( y ) ) + g ( x ′ + y ) − g ( x ) − r g ( x ′ ) g(x+y)-g(x)-r\cdot g(y)=(r-1)((r+1)^{e_0}-g(x'))+g(x'+y)-g(x')-rg(y)\\ g(x+y)-g(x)-r\cdot g(y)=(r-1)((r+1)^{e_0}-g(y))+g(x'+y)-g(x)-rg(x') g(x+y)−g(x)−r⋅g(y)=(r−1)((r+1)e0−g(x′))+g(x′+y)−g(x′)−rg(y)g(x+y)−g(x)−r⋅g(y)=(r−1)((r+1)e0−g(y))+g(x′+y)−g(x)−rg(x′) -
y
y
y的最高位低于
2
e
0
2^{e_0}
2e0,且
x
,
y
x,y
x,y相加没有进位到
2
e
0
+
1
2^{e_0+1}
2e0+1
以下两式成立:
g ( x + y ) − g ( x ) − r ⋅ g ( y ) = r ( r − 1 ) g ( y ) + r ( g ( x ′ + y ) − g ( x ′ ) − r g ( y ) ) g ( x + y ) − g ( x ) − r ⋅ g ( y ) = r ( r − 1 ) g ( x ′ ) + r ( g ( x ′ + y ) − g ( y ) − r g ( x ′ ) ) g(x+y)-g(x)-r\cdot g(y)=r(r-1)g(y)+r(g(x'+y)-g(x')-rg(y))\\ g(x+y)-g(x)-r\cdot g(y)=r(r-1)g(x')+r(g(x'+y)-g(y)-rg(x')) g(x+y)−g(x)−r⋅g(y)=r(r−1)g(y)+r(g(x′+y)−g(x′)−rg(y))g(x+y)−g(x)−r⋅g(y)=r(r−1)g(x′)+r(g(x′+y)−g(y)−rg(x′))
关于上面的式子,显然有 ( r + 1 ) e 0 − g ( x ′ ) > 0 , ( r + 1 ) e 0 − g ( y ) > 0 , g ( x ′ ) ≥ 0 , g ( y ) ≥ 0 (r+1)^{e_0}-g(x') > 0,(r+1)^{e_0}-g(y) > 0,g(x')\geq 0,g(y)\geq 0 (r+1)e0−g(x′)>0,(r+1)e0−g(y)>0,g(x′)≥0,g(y)≥0。由于我们在2,3情况中并不清楚 x ′ x' x′和 y y y的大小,所以上面的两个等式我们需要选择一个递归。如果 x , y x,y x,y都是二进制无限循环小数,则归纳会陷入循环,设 L L L表示循环节长度的 l c m lcm lcm,对于任意 0 ≤ l ≤ L 0\leq l\leq L 0≤l≤L,可以轻易得到:
g ( x 1 + y 1 ) − g ( x 1 ) − r ⋅ g ( y 1 ) ≥ r l ( g ( x 1 / 2 L + y 1 / 2 L ) − g ( x 1 / 2 L ) − r ⋅ g ( y 1 / 2 L ) ) g(x_1+y_1)-g(x_1)-r\cdot g(y_1)\geq r^l(g(x_1/2^L+y_1/2^L)-g(x_1/2^L)-r\cdot g(y_1/2^L)) g(x1+y1)−g(x1)−r⋅g(y1)≥rl(g(x1/2L+y1/2L)−g(x1/2L)−r⋅g(y1/2L))
由于 r > 1 r > 1 r>1,可以得到 g ( x 1 + y 1 ) − g ( x 1 ) − r ⋅ g ( y 1 ) ≥ 0 g(x_1+y_1)-g(x_1)-r\cdot g(y_1)\geq 0 g(x1+y1)−g(x1)−r⋅g(y1)≥0,也就是 g ( x + y ) ≥ g ( x ) + r ⋅ g ( y ) g(x+y)\geq g(x)+r\cdot g(y) g(x+y)≥g(x)+r⋅g(y)。
要求等号成立,则中间产生的 ( r + 1 ) e 0 − g ( x ′ ) , ( r + 1 ) e 0 − g ( y ) , g ( x ′ ) , g ( y ) (r+1)^{e_0}-g(x'),(r+1)^{e_0}-g(y),g(x'),g(y) (r+1)e0−g(x′),(r+1)e0−g(y),g(x′),g(y)必须全部为0。
考虑一个决策 d d d,如果它是最优决策,则 g ( n m ) = p ⋅ g ( n + d m ) + q ⋅ g ( n − d m ) g(\frac{n}{m})=p\cdot g(\frac{n+d}{m})+q\cdot g(\frac{n-d}{m}) g(mn)=p⋅g(mn+d)+q⋅g(mn−d),两边乘个 r + 1 r+1 r+1可以得到 ( r + 1 ) g ( n m ) = g ( n + d m ) + r g ( n − d m ) (r+1)g(\frac{n}{m})=g(\frac{n+d}m)+rg(\frac{n-d}{m}) (r+1)g(mn)=g(mn+d)+rg(mn−d),利用 g ( x ) g(x) g(x)的两个递推式可以知道解的分布。
将 n m \frac{n}{m} mn二进制拆分,对于所有合法的如下表示 n m = 2 e + 2 a + 2 e + b \frac{n}{m}=2^{e+2}a+2^e+b mn=2e+2a+2e+b,即第 e + 1 e+1 e+1位为空。则可能合法的 d d d有 ( 2 e ± b ) ∗ m (2^e\pm b)*m (2e±b)∗m,直接一路取模的同时把答案搞出来就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
int n,m,P;
namespace Solve_Special{
inline void main(){
int res=P==50?std::min(n,m-n):n;
printf("%.8f\n%d\n",P==50?1.0*n/m:1e-2*P,res);
if(res<=200)for(int re i=1;i<=res;++i)printf("%d ",i);
else {
for(int re i=1;i<=100;++i)printf("%d ",i);
for(int re i=99;~i;--i)printf("%d ",res-i);
}
}
}
namespace Solve_Greater{
inline void main(){
double p=P*1e-2,q=1-p,r=q/p;
double res=(1-pow(r,n))/(1-pow(r,m));
printf("%.8f\n1\n1",res);
}
}
namespace Solve_Less{
int a[107],cnt;
inline void main(){
double res=0,cur=1,p=P*1e-2,q=1-p;
for(int w=n<<1;w&&cur>1e-8;w<<=1)
if(w>=m){res+=cur*p;w-=m;cur*=q;}
else cur*=p;
printf("%.8f\n",res);
while(n){a[++cnt]=std::min(n,m-n);if(m&1)break;n%=m>>=1;}
std::sort(a+1,a+cnt+1);cnt=std::unique(a+1,a+cnt+1)-a-1;
printf("%d\n",cnt);
for(int re i=1;i<=cnt;++i)printf("%d ",a[i]);
}
}
signed main(){
#ifdef zxyoi
freopen("revenge.in","r",stdin);
#endif
scanf("%d%d%d",&P,&n,&m);
if(P==0||P==100||P==50)Solve_Special::main();
else if(P<50)Solve_Less::main();
else if(P>50)Solve_Greater::main();
else assert(0);
return 0;
}