题目
题目描述
你正在听
T
i
w
\sf Tiw
Tiw 讲题。祂有时候讲阴间生成函数,有时候讲阳间(相对而言)数学推导,当然也可能讲了些完全超出你能力范围的东西(显然此类应为绝大多数)。
你认为 T i w \sf Tiw Tiw 在每讲完一个东西之后,有 p p p 的概率立刻停止。你认为某个时刻是 “勉强知道自己在干嘛” 的时刻,当且仅当目前 T i w \sf Tiw Tiw 讲过的阳间数学推导严格多于阴间生成函数。
现在,请问在 T i w \sf Tiw Tiw 一次讲题的过程中,“勉强知道自己在干嘛” 的时刻的期望数量。可以认为 T i w \sf Tiw Tiw 每次讲一个东西的时候,有 p 1 p_1 p1 的概率是阳间的,有 p 2 p_2 p2 的概率是阴间的。(显然有 1 − p 1 − p 2 1-p_1-p_2 1−p1−p2 的概率讲了一个完全听不懂的东西。)
数据范围与提示
1
≥
p
>
0
1\ge p>0
1≥p>0 且
p
1
,
p
2
,
1
−
p
1
−
p
2
p_1,\;p_2,\;1-p_1-p_2
p1,p2,1−p1−p2 均为非负。输入的数字不超过
5
5
5 位小数。
答案的绝对误差或者相对误差需要不超过 1 0 − 8 10^{-8} 10−8 。
思路
前言:经此一题,我终于意识到,神明是存在的,祂就在我身边。我是 T L Y \sf TLY TLY 太阳神教 的忠实信徒。
那么我们直接上数学推导吧。我希望这不会让你们感到头大。
第一步,随意的考虑某个 “知道自己在跪着听” 的时刻,三种情况(阳间、阴间、听不懂)的个数,然后算概率(因为贡献是
1
1
1,概率就是期望)。
a
n
s
=
∑
a
>
b
≥
0
,
c
≥
0
p
1
a
p
2
b
(
1
−
p
1
−
p
2
)
c
(
1
−
p
)
a
+
b
+
c
−
1
(
a
+
b
+
c
a
+
b
)
(
a
+
b
a
)
ans=\sum_{a>b\ge 0,\;c\ge 0}p_1^a\;p_2^b\;(1-p_1-p_2)^c\;(1-p)^{a+b+c-1}\;{a+b+c\choose a+b}{a+b\choose a}
ans=a>b≥0,c≥0∑p1ap2b(1−p1−p2)c(1−p)a+b+c−1(a+ba+b+c)(aa+b)
神谕:“显然我们会想到把这个东西放进去。” 即,记
p
1
′
=
p
1
(
1
−
p
)
p_1'=p_1(1-p)
p1′=p1(1−p),然后
p
2
′
,
p
3
′
p_2',p_3'
p2′,p3′ 同理,那就可以直接写成
p
1
′
p_1'
p1′ 等的乘积。为了书写方便,一律去掉 '
符号,还请留心。这里
p
3
p_3
p3 对应
1
−
p
1
−
p
2
1-p_1-p_2
1−p1−p2 。
a
n
s
=
1
1
−
p
∑
a
>
b
≥
0
,
c
≥
0
p
1
a
p
2
b
(
a
+
b
a
)
p
3
c
(
a
+
b
+
c
a
+
b
)
ans={1\over 1-p}\sum_{a>b\ge 0,\;c\ge 0}p_1^a\;p_2^b\;{a+b\choose a}\;p_3^c\;{a+b+c\choose a+b}
ans=1−p1a>b≥0,c≥0∑p1ap2b(aa+b)p3c(a+ba+b+c)
这一步需要小心的是, p = 1 p=1 p=1 是有可能的!所以需要特判掉。而从这里开始,所有的 p ? p_? p? 都在 [ 0 , 1 ) [0,1) [0,1) 范围内,下面的定义域几乎都满足。
神谕:“发现
c
c
c 与另外两个独立,想办法去掉它。如果你对它比较熟悉,你可以直接指出:它就是
1
(
1
−
x
)
a
+
b
+
1
{1\over(1-x)^{a+b+1}}
(1−x)a+b+11 的生成函数开形式。” 即,
1
(
1
−
x
)
a
+
b
+
1
=
∑
i
=
0
+
∞
(
i
+
a
+
b
a
+
b
)
x
i
(
x
<
1
)
{1\over(1-x)^{a+b+1}}=\sum_{i=0}^{+\infty}{i+a+b\choose a+b}x^i\;(x<1)
(1−x)a+b+11=∑i=0+∞(a+bi+a+b)xi(x<1),令
x
=
p
3
x=p_3
x=p3,枚举变量
i
i
i 和
c
c
c 是等价的。
a
n
s
=
1
1
−
p
∑
a
>
b
≥
0
p
1
a
p
2
b
(
a
+
b
a
)
1
(
1
−
p
3
)
a
+
b
+
1
ans={1\over 1-p}\sum_{a>b\ge 0}p_1^a\;p_2^b\;{a+b\choose a}\;{1\over(1-p_3)^{a+b+1}}
ans=1−p1a>b≥0∑p1ap2b(aa+b)(1−p3)a+b+11
又到了愉快的 “塞进去” 时间!为了书写方便,仍然去掉 '
符号。
a
n
s
=
1
(
1
−
p
)
(
1
−
p
3
)
∑
a
>
b
≥
0
p
1
a
p
2
b
(
a
+
b
a
)
ans={1\over(1-p)(1-p_3)}\sum_{a>b\ge 0}p_1^a\;p_2^b\;{a+b\choose a}
ans=(1−p)(1−p3)1a>b≥0∑p1ap2b(aa+b)
做到这里,其实已经有了一个完美的解决方案。设输出的概率是
P
1
,
P
2
P_1,P_2
P1,P2,令
P
3
=
1
−
P
1
−
P
2
P_3=1-P_1-P_2
P3=1−P1−P2,式中
p
1
=
P
1
(
1
−
p
)
1
−
P
3
(
1
−
p
)
p_1={P_1(1-p)\over 1-P_3(1-p)}
p1=1−P3(1−p)P1(1−p),令
q
=
1
1
−
p
>
1
q={1\over 1-p}>1
q=1−p1>1,运用一下糖水不等式则有
p
1
=
P
1
q
−
P
3
≤
1
−
P
3
q
−
P
3
≤
w
a
t
e
r
s
u
g
a
r
1
q
=
1
−
p
p_1={P_1\over q-P_3}\le{1-P_3\over q-P_3}\overset{\overset{\scriptsize sugar}{water}}{\le}\frac{1}{q}=1-p
p1=q−P3P1≤q−P31−P3≤watersugarq1=1−p
p 2 p_2 p2 同理。而 1 − p ≤ 0.99999 1-p\le 0.99999 1−p≤0.99999,在 a + b > 3 × 1 0 6 a+b>3\times 10^6 a+b>3×106 时,幂已经小于 1 0 − 12 10^{-12} 10−12 了,不会引起误差。而 a + b ≤ 3 × 1 0 6 a+b\le 3\times 10^6 a+b≤3×106 时,可以很简单的 d p \tt dp dp 求概率。这种方法的代码我附在最后,好奇的可以自行研究。
可是这里就体现出神与人的差距了。神无意吊打我,祂只是不自知地吊打了出题人。更不幸的是: T L Y \sf TLY TLY 就是神。
记
S
a
=
∑
b
=
0
a
−
1
(
a
+
b
a
)
p
2
b
S_a=\sum_{b=0}^{a-1}{a+b\choose a}\;p_2^b
Sa=∑b=0a−1(aa+b)p2b,虽然这里并不是一个直接的生成函数展开式,但是我们可以 “错位相减”。这可能就是神的直觉吧!
S
a
−
p
2
⋅
S
a
=
∑
b
=
0
a
−
1
[
(
a
+
b
a
)
−
(
a
+
b
−
1
a
)
]
⋅
p
2
b
−
(
2
a
−
1
a
)
p
2
a
=
∑
b
=
0
a
−
1
(
a
−
1
+
b
a
−
1
)
⋅
p
2
b
−
(
⋯
)
=
S
a
−
1
+
(
2
a
−
2
a
−
1
)
p
2
a
−
1
−
(
⋯
)
S_a-p_2\cdot S_a=\sum_{b=0}^{a-1}\left[{a+b\choose a}-{a+b-1\choose a}\right]\cdot p_2^b-{2a-1\choose a}p_2^{a}\\ =\sum_{b=0}^{a-1}{a-1+b\choose a-1}\cdot p_2^b-(\cdots)=S_{a-1}+{2a-2\choose a-1}{p_2}^{a-1}-(\cdots)
Sa−p2⋅Sa=b=0∑a−1[(aa+b)−(aa+b−1)]⋅p2b−(a2a−1)p2a=b=0∑a−1(a−1a−1+b)⋅p2b−(⋯)=Sa−1+(a−12a−2)p2a−1−(⋯)
为了防止过长,有所省略。令
r
a
=
(
2
a
−
2
a
−
1
)
p
2
a
−
1
−
(
2
a
−
1
a
)
p
2
a
r_a={2a-2\choose a-1}p_2^{a-1}-{2a-1\choose a}p_2^a
ra=(a−12a−2)p2a−1−(a2a−1)p2a
递推式简写为
S
a
=
S
a
−
1
+
r
a
1
−
p
2
S_a={S_{a-1}+r_a\over 1-p_2}
Sa=1−p2Sa−1+ra
一个很不错的
o
b
s
e
r
v
a
t
i
o
n
\rm observation
observation 是
1
−
p
2
1-p_2
1−p2 是一个定值。再考虑到它的递归出口
{
S
0
=
0
r
1
=
1
−
p
2
\begin{cases}S_0=0\\r_1=1-p_2\end{cases}
{S0=0r1=1−p2,显然我们可以直接考虑每个
r
a
r_a
ra 最后得到的系数。所以
(
1
−
p
)
(
1
−
p
3
)
⋅
a
n
s
=
∑
a
=
1
+
∞
p
1
a
S
a
=
∑
a
=
1
+
∞
p
1
a
∑
i
=
1
a
r
i
(
1
−
p
2
)
a
−
i
+
1
=
∑
i
=
1
+
∞
r
i
p
1
i
∑
a
=
i
+
∞
p
1
a
−
i
(
1
−
p
2
)
a
−
i
+
1
=
1
1
−
p
2
∑
i
=
1
+
∞
r
i
p
1
i
1
−
p
2
1
−
p
2
−
p
1
=
1
1
−
p
1
−
p
2
∑
i
=
1
+
∞
r
i
p
1
i
(1-p)(1-p_3)\cdot {\rm ans}=\sum_{a=1}^{+\infty}p_1^a\;S_a\\ =\sum_{a=1}^{+\infty}p_1^a\sum_{i=1}^{a}\frac{r_i}{(1-p_2)^{a-i+1}}\\ =\sum_{i=1}^{+\infty}r_i\;p_1^i\sum_{a=i}^{+\infty}\frac{p_1^{a-i}}{(1-p_2)^{a-i+1}}\\ ={1\over 1-p_2}\sum_{i=1}^{+\infty}r_i\;p_1^i\;{1-p_2\over 1-p_2-p_1}\\ =\frac{1}{1-p_1-p_2}\sum_{i=1}^{+\infty} r_i\;p_1^i
(1−p)(1−p3)⋅ans=a=1∑+∞p1aSa=a=1∑+∞p1ai=1∑a(1−p2)a−i+1ri=i=1∑+∞rip1ia=i∑+∞(1−p2)a−i+1p1a−i=1−p21i=1∑+∞rip1i1−p2−p11−p2=1−p1−p21i=1∑+∞rip1i
那个地方,公比 p 1 1 − p 2 p_1\over 1-p_2 1−p2p1 可靠吗?当然。因为 p 1 + p 2 < 1 p_1+p_2<1 p1+p2<1,具体原因就去 s u g a r w a t e r \rm sugar\;water sugarwater 那一节比对吧。
于是我们竟然只需要求
∑
i
=
1
+
∞
r
i
p
1
i
\sum_{i=1}^{+\infty}r_i\;p_1^i
∑i=1+∞rip1i 了耶!两个组合数分开考虑。第一项:
∑
i
=
1
+
∞
(
2
i
−
2
i
−
1
)
p
2
i
−
1
p
1
i
\sum_{i=1}^{+\infty}{2i-2\choose i-1}\;p_2^{i-1}\;p_1^i
i=1∑+∞(i−12i−2)p2i−1p1i
这东西似乎和卡特兰数很像。考虑用生成函数化简,记
C
1
(
x
)
=
∑
i
=
0
+
∞
(
2
i
i
)
⋅
x
i
C_1(x)=\sum_{i=0}^{+\infty}{2i\choose i}\cdot x^i
C1(x)=i=0∑+∞(i2i)⋅xi
同样试着写递推式。现实意义仍然是
n
n
n 个
0
0
0、
n
n
n 个
1
1
1 的序列。还是可以考虑前缀
0
,
1
0,1
0,1 数量相同之处。不妨设第一位是
0
0
0,将其反转就是第一位为
1
1
1 的。那么前半部分是卡特兰数,后半部分还是
C
1
C_1
C1 。记卡特兰数的生成函数
1
−
1
−
4
x
2
x
=
C
0
(
x
)
\frac{1-\sqrt{1-4x}}{2x}=C_0(x)
2x1−1−4x=C0(x),则有
C
1
(
x
)
=
2
x
⋅
C
0
(
x
)
⋅
C
1
(
x
)
+
1
⇒
C
1
(
x
)
=
1
1
−
4
x
C_1(x)=2x\cdot C_0(x)\cdot C_1(x)+1\\ \Rightarrow C_1(x)=\frac{1}{\sqrt{1-4x}}
C1(x)=2x⋅C0(x)⋅C1(x)+1⇒C1(x)=1−4x1
第二项是要减去的,绝对值为
∑
i
=
1
+
∞
(
2
i
−
1
i
)
⋅
p
2
i
⋅
p
1
i
C
2
(
x
)
=
∑
i
=
1
+
∞
(
2
i
−
1
i
)
⋅
x
i
\sum_{i=1}^{+\infty}{2i-1\choose i}\cdot p_2^i\cdot p_1^i\\ C_2(x)=\sum_{i=1}^{+\infty}{2i-1\choose i}\cdot x^i
i=1∑+∞(i2i−1)⋅p2i⋅p1iC2(x)=i=1∑+∞(i2i−1)⋅xi
稍微麻烦点。它是在坐标系内行走到
(
i
,
i
−
1
)
(i,i-1)
(i,i−1) 的方案数。如果第一步是向右,那么它就是上面的
C
1
C_1
C1 的情形;如果第一步是向上,那么必然经过
ℓ
:
x
=
y
\ell:x=y
ℓ:x=y 这条直线,分成两部分,前半部分是卡特兰数,后半部分又是
C
2
C_2
C2 。所以
C
2
(
x
)
=
x
⋅
C
1
(
x
)
+
x
⋅
C
0
(
x
)
⋅
C
2
(
x
)
⇒
C
2
(
x
)
=
2
x
1
−
4
x
+
1
−
4
x
C_2(x)=x\cdot C_1(x)+x\cdot C_0(x)\cdot C_2(x)\\ \Rightarrow C_2(x)={2x\over{1-4x+\sqrt{1-4x}}}
C2(x)=x⋅C1(x)+x⋅C0(x)⋅C2(x)⇒C2(x)=1−4x+1−4x2x
于是终于写出
a
n
s
=
p
1
⋅
C
1
(
p
1
p
2
)
−
C
2
(
p
1
p
2
)
(
1
−
p
)
(
1
−
p
3
)
(
1
−
p
1
−
p
2
)
ans={p_1\cdot C_1(p_1p_2)-C_2(p_1p_2)\over(1-p)(1-p_3)(1-p_1-p_2)}
ans=(1−p)(1−p3)(1−p1−p2)p1⋅C1(p1p2)−C2(p1p2)
当然,这里
p
1
p
2
≤
1
4
p_1p_2\le\frac{1}{4}
p1p2≤41 才行欸!其实这是成立的。因为
{
p
1
=
P
1
(
1
−
p
)
1
−
(
1
−
P
1
−
P
2
)
(
1
−
p
)
p
2
=
P
2
(
1
−
p
)
1
−
(
1
−
P
1
−
P
2
)
(
1
−
p
)
p
3
=
(
1
−
P
1
−
P
2
)
(
1
−
p
)
\begin{cases}p_1={P_1(1-p)\over 1-(1-P_1-P_2)(1-p)}\\p_2={P_2(1-p)\over 1-(1-P_1-P_2)(1-p)}\\p_3=(1-P_1-P_2)(1-p)\end{cases}
⎩⎪⎨⎪⎧p1=1−(1−P1−P2)(1−p)P1(1−p)p2=1−(1−P1−P2)(1−p)P2(1−p)p3=(1−P1−P2)(1−p),那么你很容易发现
q
=
1
1
−
p
>
1
,
P
3
=
1
−
P
1
−
P
2
≤
1
p
1
p
2
=
P
1
P
2
(
q
−
P
3
)
2
≤
A
.
G
.
(
P
1
+
P
2
2
)
2
(
q
−
P
3
)
2
≤
(
1
−
P
3
)
2
4
(
q
−
P
3
)
2
≤
w
a
t
e
r
s
u
g
a
r
1
4
q
2
<
1
4
q=\frac{1}{1-p}>1,\;P_3=1-P_1-P_2\le 1\\ p_1p_2=\frac{P_1P_2}{(q-P_3)^2}\overset{A.G.}{\le}\frac{({P_1+P_2\over 2})^2}{(q-P_3)^2}\le\frac{(1-P_3)^2}{4(q-P_3)^2}\overset{\overset{\scriptsize sugar}{water}}{\le }\frac{1}{4q^2}<\frac{1}{4}
q=1−p1>1,P3=1−P1−P2≤1p1p2=(q−P3)2P1P2≤A.G.(q−P3)2(2P1+P2)2≤4(q−P3)2(1−P3)2≤watersugar4q21<41
如果你愿意的话,你也可以把 a n s ans ans 写成关于输入数据的式子。
就这样,这道题就被 无精度误差(除了 d o u b l e \tt double double 自动丢失精度)算出来了。 T L Y \sf TLY TLY 太阳神 永生!
代码
#include <bits/stdc++.h>
int main(){
freopen("augury.in","r",stdin);
freopen("augury.out","w",stdout);
double p1, p2, p3, p; int T;
for(scanf("%*d%d",&T); T; --T){
scanf("%lf %lf %lf",&p1,&p2,&p);
if(p == 1){ // must end immediately
printf("%.10f\n",p1); continue;
} else p3 = 1-p1-p2;
double ans = 1/(1-p)/(1-p3*(1-p));
p1 /= 1/(1-p)-p3, p2 /= 1/(1-p)-p3;
double k = 1-4*p1*p2; // 1-4x
ans *= p1/sqrt(k)-2*p1*p2/(k+sqrt(k));
printf("%.10f\n",ans/(1-p1-p2));
}
return 0;
}
以及那个卡精度的方法:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
double qkpow(double b,int q){
double a = 1;
for(; q; q>>=1,b*=b)
if(q&1) a *= b;
return a;
}
const int MaxN = 3000005;
double f[MaxN];
int main(){
readint();
for(int T=readint(); T; --T){
double p1, p2, p;
scanf("%lf %lf %lf",&p1,&p2,&p);
if(p == 1){ // must end immediately
printf("%.10f\n",p1);
continue;
}
double p3 = 1-p1-p2;
p1 *= (1-p), p2 *= (1-p), p3 *= (1-p);
p1 /= (1-p3), p2 /= (1-p3);
f[0] = 0, f[1] = p1, f[2] = p1*p1;
// tmp = C(i-1,i/2)*pow(p1,i/2)*pow(p2,i/2)
double tmp = p1*p2;
double ans = f[1]+f[2];
for(int i=3; i<MaxN; ++i){
if(!(i&1)) tmp *= p1*p2*
(i-1-(i>>1))/(i>>1);
tmp *= (i-1); tmp /= (i-1-(i/2));
if(i&1) f[i] = f[i-1]*(p1+p2)+tmp*p1;
else f[i] = f[i-1]*(p1+p2)-tmp;
ans += f[i]; // add them up
}
ans /= (1-p), ans /= (1-p3);
printf("%.10f\n",ans);
}
return 0;
}