题意:给你三个杯子,然后把要猜的东西放在中间,然后随意把左右的杯子拿来交换,做n次,然后最后猜的是中间,问你中的概率?这个n很大,他给你k个数,a1……ak,这k个数乘起来表示n。
思路
因为这是一个概率问题,所以先按照状态转移的思想来思考。如果将初始状态定义为 (1,2,3) ,那么状态空间将只有 6 个元素—— (1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1) 。但是将状态转移图画好后又会发现没有什么我们需要的线索。那么换一种状态定义方式呢?假设状态 i 为 i 次交换后的状态,令 d[i] 为 i 次交换后初始状态下的中间的杯子(我们可以暂时称之为第 2 个杯子)依然在中间的概率。我们试图寻找 d[i] 与 d[i−1] 的关系。不难发现,当状态为 i−1 时第 2 个杯子在中间的话,到了状态为 i 的时候第 2 个杯子就不可能在中间了。于是有如下关系
d[i]=1−d[i−1]2
因为要得到最最简分数形式的概率,因此光得到递推公式是不够的,我们要求一个能直接计算出结果的封闭形式。于是用待定系数法可以得到一个等比数列的递推公式
d[i]−13=−12(d[i−1]−13)
解这个递推公式得
d[n]=(−1)n+2n−13×2n−1
凑巧的是, (−1)n+2n−1 正好是 3 的倍数(与 2 的幂的相邻的两个数中一定有一个是 3 的倍数)而且 (−1)n+2n−1 除以 3 一定能得到奇数,这样就能与上式分母的偶数互质了。所以有
p=(−1)n+2n−13
q=2n−1
题目就得解了。另外要注意的是,对分数取 mod 的时候要对分母计算模逆元,也就是要算出 3 的模逆元。计算 2n−1 的时候可以用快速幂算法来算。
最后,偶然发现像P这样的数被称为 Jacobsthalnumbers (维基百科)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
bool one = true;
int k, odd = 1;
ll a, p, q = 2;
// 快速幂算法
ll modPow(ll a, ll n, ll mod) {
ll ans = 1;
for(; n > 0; n >>= 1) {
if(n & 1) {
ans = (ans * a) % mod;
}
a = (a * a) % mod;
}
return ans;
}
// 求模逆元
ll modInv(ll a, ll mod) {
return modPow(a, mod - 2, mod);
}
int main() {
scanf("%d", &k);
for(int i = 0; i < k; i++) {
scanf("%I64d", &a);
odd &= (a & 1);
q = modPow(q, a, mod);
if(a > 1) {
one = false;
}
}
q = (q * modInv(2, mod)) % mod;
if(one == true) {
p = 0;
}
else {
p = q;
p += (odd ? -1 : 1);
p = (p * modInv(3, mod)) % mod;
}
printf("%I64d/%I64d\n", p, q);
return 0;
}