题意:
有
n
n
n个洞穴,其中一个有宝藏。
你每天有
k
k
k次机会去洞穴中找宝藏,如果你去到的洞穴中有宝藏则有
1
2
\frac{1}{2}
21的概率找到。每次寻找的概率是独立计算的。
问找到宝藏的期望天数。
数据范围:
k ≤ n ≤ 5 ∗ 1 0 8 k \leq n \leq 5*10^8 k≤n≤5∗108
Analysis:
发现这
k
k
k次机会一定是有决策的,在随机意义下,我们选择尽量少的去找宝藏,那么就是
k
k
k个
k
k
k个选。在这样的情况下,无限找的话,每
g
c
d
(
n
,
k
)
gcd(n,k)
gcd(n,k)个地方的期望找到的天数一样,我们可以将
n
,
k
n,k
n,k同时除
g
c
d
gcd
gcd答案不变。
设
f
i
f_i
fi表示在第
i
i
i个洞穴找到宝藏的期望天数,那么答案就是
∑
f
i
n
\frac{\sum f_i}{n}
n∑fi。
我们发现对于前
n
−
k
n-k
n−k个洞穴,会有
f
i
+
k
=
f
i
+
1
f_{i+k}=f_i+1
fi+k=fi+1。因为每一次找
i
i
i前会先找
f
i
−
k
f_{i-k}
fi−k。
对于后
k
k
k个洞穴,会有
f
i
+
k
−
n
=
(
1
−
p
)
∗
(
f
i
+
1
)
+
p
=
(
1
−
p
)
∗
f
i
+
1
f_{i+k-n}=(1-p)*(f_i+1)+p=(1-p)*f_{i}+1
fi+k−n=(1−p)∗(fi+1)+p=(1−p)∗fi+1。前一次没找到加上第一次就找到的概率。我们把这两种转移看做一种
A
,
B
A,B
A,B变换
我们考虑把答案如下表达:
a
n
s
=
∑
i
=
0
k
−
1
S
1
(
f
i
)
+
∑
i
=
k
n
−
1
S
2
(
f
i
)
ans=\sum_{i=0}^{k-1}S1(f_i)+\sum_{i=k}^{n-1}S2(f_i)
ans=i=0∑k−1S1(fi)+i=k∑n−1S2(fi)
以上的
A
,
B
,
S
1
,
S
2
A,B,S1,S2
A,B,S1,S2都是形如
k
x
+
b
kx+b
kx+b的一次函数形式的变换。
考虑对前
k
k
k个洞穴讨论,能够得到:
设
n
n
n%
k
=
k
′
k=k'
k=k′。
当
i
i
i在
[
k
′
,
k
)
[k',k)
[k′,k) 有
f
i
+
k
′
−
k
=
B
′
=
A
−
n
k
(
B
−
(
f
i
)
)
f_{i+k'-k}=B'=A^{-\frac{n}{k}}(B^-(f_i))
fi+k′−k=B′=A−kn(B−(fi))。考虑第
i
i
i个转移多少次得到
f
i
+
k
′
−
k
f_{i+k'-k}
fi+k′−k,然后对于转移做逆变换。
同理,对于
i
i
i在
[
0
,
k
′
)
[0,k')
[0,k′),有
f
i
+
k
′
=
A
′
=
A
−
n
k
−
1
(
B
−
(
f
i
)
)
f_{i+k'}=A'=A^{-\frac{n}{k}-1}(B^-(f_i))
fi+k′=A′=A−kn−1(B−(fi))。
我们把求
f
i
f_i
fi的部分就转换成了求
n
=
k
,
k
=
k
′
n=k,k=k'
n=k,k=k′的子问题。
A
′
,
B
′
A',B'
A′,B′为新的
A
,
B
A,B
A,B。
考虑求和部分也转换一下。
考虑把后面转移的
A
A
A全部加起来,可以得到:
S
1
=
S
1
+
∑
i
=
0
n
k
S
2
(
A
i
)
S1=S1+\sum_{i=0}^{\frac{n}{k}}S2(A^i)
S1=S1+i=0∑knS2(Ai)
S
2
=
S
1
+
∑
i
=
0
n
k
−
1
S
2
(
A
i
)
S2=S1+\sum_{i=0}^{\frac{n}{k}-1}S2(A^i)
S2=S1+i=0∑kn−1S2(Ai)
然后求复合函数的次幂部分,由于其满足结合律,我们可以快速幂。
那么这题就做完了,最后当
k
=
0
k=0
k=0时,解个
x
=
k
x
+
b
x=kx+b
x=kx+b的方程即可。
复杂度
O
(
T
log
n
)
O(T\log n)
O(Tlogn)。
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
typedef long long ll;
const ll mo = 1e9 + 7;
const ll inv2 = mo - mo / 2;
struct node
{
ll k,b;
node operator * (node r) const
{ return (node){k * r.k % mo,(b + k * r.b) % mo}; }
node operator + (node r) const
{ return (node){(k + r.k) % mo,(b + r.b) % mo}; }
};
int T;
ll n,k,p;
inline ll gcd(ll a,ll b) { return !b ? a : gcd(b,a % b); }
inline ll pow(ll x,ll p)
{
ll ret = 1;
for (; p ; p >>= 1,x = x * x % mo)
if (p & 1) ret = ret * x % mo;
return ret;
}
inline node C(node x,ll p)
{
node ret = (node){1,0};
for (; p ; p >>= 1,x = x * x)
if (p & 1) ret = x * ret;
return ret;
}
inline node G(node x,ll p)
{
if (!p) return (node){0,0};
if (x.k == 1) return (node){p,p * (p + 1) % mo * inv2 % mo * x.b % mo};
node a = C(x,p + 1),z; a.k = (a.k - x.k + mo) % mo,a.b = (a.b - x.b + mo) % mo;
z.k = a.k * pow(x.k + mo - 1,mo - 2) % mo;
z.b = (z.k - p + mo) * pow(x.k + mo - 1,mo - 2) % mo * x.b % mo;
return z;
}
inline ll solve(ll n,ll k,node A,node B,node s1,node s0)
{
if (!k) return A.b * pow(mo + 1 - A.k,mo - 2) % mo * s0.k % mo;
node ns1 = s1 + (s0 * G(A,n / k)),ns0 = s1 + (s0 * G(A,n / k - 1));
node nA = B; ll ni = pow(nA.k,mo - 2); nA.k = ni,nA.b = (mo - nA.b * ni % mo) % mo;
node nB = nA; ni = pow(A.k,mo - 2); A.k = ni,A.b = (mo - A.b * ni % mo) % mo;
nA = C(A,n / k - 1) * nA,nB = C(A,n / k) * nB;
ll S = ((n % k) * ns1.b % mo + (k - n % k) * ns0.b % mo) % mo; ns1.b = ns0.b = 0;
return (solve(k,n % k,nA,nB,ns1,ns0) + S) % mo;
}
int main()
{
// freopen("a.in","r",stdin);
scanf("%d",&T);
while (T--)
{
scanf("%lld%lld",&n,&k); p = inv2;
ll d = gcd(n,k); n /= d,k /= d;
printf("%lld\n",solve(n,k,(node){1,1},(node){mo + 1 - p,1},(node){1,0},(node){1,0}) * pow(n,mo - 2) % mo);
}
return 0;
}