Link
最优的决策肯定是尽量均摊,不妨从前往后做,那么我们可以将(n,k)都除以(gcd(n,k))。
设(f_i)表示在第(i)个洞穴找到宝藏的期望天数,那么答案为(frac{sum f_i}n)。
考虑(f_i)的转移式:
[f_i=
egin{cases}
f_{i-k}+1&iin[k,n)\
(1-p)f_i+1&iin[0,k)
end{cases}
]
记(A(f_i)=f_{i+k},B(f_i)=f_{i+k-n}),那么我们要求的就是(ans=sumlimits_{i=0}^{k-1}S1(f_i)+sumlimits_{i=k}^{n-1}S2(f_i))。
考虑将((n,k))递归至((k,k'=nmod k))的子问题,此时(ans=sumlimits_{i=0}^{k'-1}S1'(f_i)+sumlimits_{i=k}^{n-1}S2'(f_i))。
不难发现(S_1'=S1+sumlimits_{i=1}^{lfloorfrac nk
floor}S2(A^i),S2'=S1+sumlimits_{i=1}^{lfloorfrac nk
floor-1}S2(A^i))。
注意到(f_{i+k-k'}=B(A^{lfloorfrac nk
floor}(f_i))),因此(f_{i+k'-k}=A^{-lfloorfrac nk
floor}(B^{-1}(f_i))),这就是(B')。
同理可以推出(A')为(f_{i+k'}=A^{-lfloorfrac nk
floor-1}(B^{-1}(f_i)))。
#include<cstdio>
#include<numeric>
using i64=long long;
const i64 P=1000000007,i2=P-P/2;
int read(){int x;scanf("%d",&x);return x;}
i64 pow(i64 a,i64 k){i64 r=1;for(;k;k>>=1,a=a*a%P)if(k&1)r=r*a%P;return r;}
i64 f(i64 x,i64 y){return y<=0? 0:x==1? y:pow(x-1,P-2)*(pow(x,y)-1)%P;}
i64 g(i64 x,i64 y){return y<=0? 0:x==1? y*(y+1)/2%P:pow(x-1,P-2)*((pow(x,y)-1)*pow(x-1,P-2)%P*x%P-y+P)%P;}
i64 solve(i64 n,i64 k,i64 a1,i64 a0,i64 b1,i64 b0,i64 s,i64 s1,i64 s2)
{
if(n==1) return (s1*b0%P*pow(1-b1+P,P-2)+s)%P;
i64 q=(n-1)/k,r=n-q*k,x,y,A1,A0,B1,B0,S,S1,S2;
x=b1*pow(a1,q-1)%P,y=(a0*b1%P*f(a1,q-1)+b0)%P,A1=pow(x,P-2),A0=P-y*A1%P;
x=b1*pow(a1,q)%P,y=(a0*b1%P*f(a1,q)+b0)%P,B1=pow(x,P-2),B0=P-y*B1%P;
S=(s2*a0%P*(r*f(a1,q)%P+k*g(a1,q-1)%P)+s)%P,S1=(s2*a1%P*f(a1,q)+s1)%P,S2=(s2*a1%P*f(a1,q-1)+s1)%P;
return solve(k,r,A1,A0,B1,B0,S,S1,S2);
}
void solve()
{
i64 n,k,d;
n=read(),k=read(),d=std::gcd(n,k),n/=d,k/=d;
printf("%lld
",solve(n,k,1,1,i2,1,0,1,1)*pow(n,P-2)%P);
}
int main(){for(int t=read();t;--t) solve();}