[博客迁移][题解] 2017 ACM-ICPC 亚洲区(西安赛区)网络赛 B. Coin

225 阅读4分钟

from 2017 ACM-ICPC 亚洲区(西安赛区)网络赛 B. Coin


题目:nanti.jisuanke.com/t/17115

Bob has a not even coin, every time he tosses the coin, the probability that the coin's front face up is \frac{q}{p}(\frac{q}{p} \le \frac{1}{2})

The question is, when Bob tosses the coin kk times, what's the probability that the frequency of the coin facing up is even number.

If the answer is \frac{X}{Y}​​ , because the answer could be extremely large, you only need to print (X * Y^{-1}) \mod (10^9+7)

Input Format

First line an integer TT, indicates the number of test cases (T \le 100).

Then Each line has 33 integer p,q,k(1\le p,q,k \le 10^7) indicates the i-th test case.

Output Format

For each test case, print an integer in a single line indicates the answer.

样例输入

2
2 1 1
3 1 2

样例输出

500000004
555555560

题意:

扔 k次硬币,给你一次硬币向上的概率为p\over q,让你求k次硬币后向上的次数为偶数的概率

刚开始不理解样例结果是怎么出来的,后来队友提醒我-1代表的不是取倒数而是求逆元

方法一

运用高中知识,设向上概率是a,向下为b,则答案为:

C^{0}_{k} *a^0 * b^k  + C^{2}_{k} *a^2 *b^{k-2} +C_{k}^{4}*a^4 *b^{k-4} ......... + C_{k}^{k}*a^k*b^0

暴力去求会各种超,超内存(因为要取模)、超时间

因为

(a+b)^k 展开为: C_k^{0}*a^0 *b^k + C^{1}_k*a^1*b^{k-1}.......+C_k^k*a^k*b^0

(a-b) ^k 展开为:
C_k^0 *a^0 *(-b)^k +C_{k}^1*a* (-b)^{k-1} .........+C_k^k*a^k*(-b)^0

所以答案可以转化成: ((a+b)^k +(a-b)^k ) \over 2

代码:

// @Team    : nupt2017team12
// @Author  : Zst
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
#define LL long long
#define MOD 1000000007
#define CLR(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define pb push_back
#define FOR(i,a,b) for( int i = ( a ); i <= ( b ); ++i )

LL poww( LL x, int n ) {
    LL base = x;
    LL tmp = 1;
    while( n ) {
        if( ( n & 1 ) ) {
            tmp = ( tmp * base ) % MOD;
        }
        base = ( base * base ) % MOD;
        n = n >> 1;
    }
    return tmp;
}

int p, q, k;


int main()
{
    // freopen( "B.txt", "r", stdin );
    int w;
    scanf( "%d", &w );
    while( w-- ) {
        scanf( "%d%d%d", &p, &q, &k );
        LL ans = ( poww( p, k ) + poww( p-2*q, k ) ) % MOD;
        LL a = poww( p, k ) * 2;
        ans *= ( poww( a, MOD-2 ) ) % MOD;
        printf( "%d\n",int( ans%MOD ) );
    }
    return 0;
}

方法二

使用矩阵快速幂

初始化二维矩阵a:

\begin{bmatrix} 
{{p-q} \over p}  &   { p \over q} \\
{p \over q} &  {{p-q} \over p}
\end{bmatrix}

其中a[0][0]表示抛一次反面向上的概率(即正面向上0次),a[0][1]表示抛一次正面向上的概率(即正面向上1次),a[1][0]、a[1][1]同理

那么抛两次,正面向上偶数次的概率是多少呢?

两种情况

  • 第一种:第一次反面朝上(0次),第二次也是反面朝上(0次),0+0为偶数
  • 第二种:第一次正面朝上(1次),第二次正面朝上(1次),1+1为偶数

a * a 得到的矩阵 a_1 的[0][0]元素就是我们要的概率

a_1[0][0] = a[0][0] * a[0][0] + a[0][1] * a[1][0]

此时a_1[0][0] 表示偶数次向上的概率,a_1[0][1]表示奇数次向上的概率

求抛三次

a_1a 得到的新矩阵b的[0][0]元素就是答案

b[0][0] = a_1[0][0] * a[0][0] + a_1[0][1] * a[1][0]

偶数次向上 乘上 0次正面向上,得到的结果还是偶数次向上 奇数次向上 乘上 1次正面向上,得到的结果变成偶数次向上的概率

注意,按照题目中的要求,分母要变成乘上逆元

代码:

// @Team    : nupt2017team12
// @Author  : Zst
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
#define LL long long
#define MOD 1000000007
#define CLR(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define pb push_back
#define FOR(i,a,b) for( int i = ( a ); i <= ( b ); ++i )

struct Matrix {
    LL matrix[2][2];
    Matrix() {
        CLR( matrix, 0 );
        FOR( i, 0, 1 ) {
            matrix[i][i] = 1;
        }
    }
    Matrix( const LL matrix[2][2] ) {
        FOR( i, 0, 1 ) {
            FOR( j, 0, 1 ) {
                this->matrix[i][j] = matrix[i][j];
            }
        }
    }
    Matrix operator*( const Matrix& a ) {
        Matrix res;
        FOR( i, 0, 1 ) {
            FOR( j, 0, 1 ) {
                res.matrix[i][j] = 0;
                FOR( k, 0, 1 ) {
                    res.matrix[i][j] = ( res.matrix[i][j] +  a.matrix[i][k] * this->matrix[k][j] ) % MOD;
                }
            }
        }
        return res;
    }
};

int p, q, k;

Matrix poww( const Matrix& a, int n ) {
    Matrix base( a.matrix );
    Matrix res;
    while( n ) {
        if( ( n & 1 ) ) {
            res = res * base;
        }
        n = n >> 1;
        base = base * base;
    }
    return res;
}

LL poww( LL a, int n ) {
    LL base = a;
    LL res = 1;
    while( n ) {
        if( ( n & 1 ) ) {
            res = ( res * base ) % MOD;
        }
        base = ( base * base ) % MOD;
        n = n >> 1;
    }
    return res;
}


int main()
{
    // freopen( "B.txt", "r", stdin );
    int w;
    scanf( "%d", &w );
    while( w-- ) {
        scanf( "%d%d%d", &p, &q, &k );
        LL a = ( ( p - q ) * poww( p, MOD-2 ) ) % MOD;
        LL b = ( q * poww( p, MOD-2 ) ) % MOD;
        LL tmp[2][2] = {
            { a, b },
            { b, a }
        };
        Matrix res( tmp );
        res = poww( res, k );
        printf( "%d\n",int( res.matrix[0][0] ) );
    }
    return 0;
}