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

转载请注明出处,http://blog.csdn.net/Bule_Zst/article/details/78024120


题目:https://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 qp(qp12)

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 XY ​​ , because the answer could be extremely large, you only need to print (XY1)mod(109+7)

Input Format

First line an integer TT, indicates the number of test cases (T100) .

Then Each line has 33 integer p,q,k(1p,q,k107) 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次硬币,给你一次硬币向上的概率为 pq ,让你求k次硬币后向上的次数为偶数的概率

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

方法一:

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

C0ka0bk+C2ka2bk2+C4ka4bk4.........+Ckkakb0

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

因为

(a+b)k 展开为:
C0ka0bk+C1ka1bk1.......+Ckkakb0

(ab)k 展开为:
C0ka0(b)k+C1ka(b)k1.........+Ckkak(b)0

所以答案可以转化成: ((a+b)k+(ab)k)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

pqp   pq

pq     pqp

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

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

两种情况

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

aa 得到的矩阵 a1 的[0][0]元素就是我们要的概率

a1[0][0]=a[0][0]a[0][0]+a[0][1]a[1][0]

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

求抛三次

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

b[0][0]=a1[0][0]a[0][0]+a1[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值