哈尔滨理工大学第12届程序设计竞赛 B 抓球

44 篇文章 4 订阅

链接:https://ac.nowcoder.com/acm/contest/30825/B

题目描述
一个箱子里有n个黑球,m个白球,小王想要连续q次从箱子里随机的取出k个球(每次取出k个后立即放回),连续q次取球k个都为黑球的概率是多少,结果对1e9+7取模。

输入描述:
第一行给出一个t,代表t组测试数据。(1<=t<=100000)

每组测试数据给出n,m,k,q。(1<=n,m,k<=1e5,k<=n+m,1<=q<=1e12)

输出描述:
对于每组测试数据输出一个结果代表概率。

示例1
输入

2
2 2 1 2
3 3 2 1

输出

250000002
400000003

说明
第一个样例结果为1/4,取模为1*4^(mod-2)%mod=250000002。

前置知识

一、快速求组合数(非必要,可以跳过)

需要用到乘法逆元、费马小定理

ll qpow(ll a,ll b,ll mod){//快速幂
    ll ans=1,base=a;
    while(b){
        if(b&1) ans=ans*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return ans;
}

ll C(ll n,ll m,ll p){//求组合数
    if(n<m) return 0;
    if(m>n-m) m=n-m;
    ll a=1,b=1;
    FOR(i,0,m-1){
        a=(a*(n-i))%p;
        b=(b*(i+1))%p;
    }
    return a*qpow(b,p-2,p)%p;//乘法逆元
}
二、乘法逆元

定义:如果一个线性同余方程 a x ≡ 1 ( m o d p ) ax \equiv 1 \pmod p ax1(modp),则 x x x 称为 a   m o d   p a \bmod p amodp 的逆元,记作 a − 1 a^{-1} a1

下面给出一种求法:( p p p 必须是素数)

因为 a x ≡ 1 ( m o d p ) ax \equiv 1 \pmod p ax1(modp)
所以 a x ≡ a p − 1 ( m o d p ) ax \equiv a^{p-1} \pmod p axap1(modp)(费马小定理);
所以 x ≡ a p − 2 ( m o d p ) x \equiv a^{p-2} \pmod p xap2(modp)

于是可以用快速幂来求。

a_reverse=qpow(a,p-2,p);

思路

通过加乘原理可以推算出答案:
a n s = [ n ! ( n + m − k ) ! ( n − k ) ! ( n + m ) ! ] q ans = {[\frac{n!(n+m-k)!}{(n-k)!(n+m)!}]}^{q} ans=[(nk)!(n+m)!n!(n+mk)!]q
或通过排列组合知识推算出:
a n s = [ C k n C k n + m ] q ans = {[\frac{\mathrm C_k^n}{\mathrm C_k^{n+m}}]}^{q} ans=[Ckn+mCkn]q
第一种答案可以通过第二种答案化简得出。

分母转换成逆元,与分子相乘,再一起快速幂即可。

但是这道题不能直接算组合数,会超时。

TLE 代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
#define ll long long

const int mod=1e9+7;

ll qpow(ll a,ll b,ll mod){//a^b
    ll ans=1,base=a;
    while(b){
        if(b&1) ans=ans*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return ans;
}

ll C(ll n,ll m,ll p){
    if(n<m) return 0;
    if(m>n-m) m=n-m;
    ll a=1,b=1;
    FOR(i,0,m-1){
        a=(a*(n-i))%p;
        b=(b*(i+1))%p;
    }
    return a*qpow(b,p-2,p)%p;
}

int main(){
	cin.tie(0)->sync_with_stdio(0);
	int T;cin>>T;
	while(T--){
		ll n,m,k,q;
		cin>>n>>m>>k>>q;
		ll A=C(n,k,mod);
		ll B=C(n+m,k,mod);
		B=qpow(B,mod-2,mod);
		ll ans=qpow(A*B%mod,q,mod);
		cout<<ans<<'\n';
	}
	return 0;
}

应该预处理阶乘数组,由于模数不大,不用高精度

由于 1<=n,m,k<=1e5, k<=n+m ,数组开 2e5 大小即可

AC 代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
#define ll long long

const int mod=1e9+7;
const int maxn=2e5+7;

ll fa[maxn];

ll qpow(ll a,ll b,ll mod){//a^b
    ll ans=1,base=a;
    while(b){
        if(b&1) ans=ans*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return ans;
}

void init(){
	fa[0]=fa[1]=1;//f[0]也要赋值
	FOR(i,2,maxn-1)
		fa[i]=fa[i-1]*i%mod;
}

int main(){
	cin.tie(0)->sync_with_stdio(0);
	init();
	int T;cin>>T;
	while(T--){
		ll n,m,k,q;
		cin>>n>>m>>k>>q;
		if(n-k<0){cout<<"0\n";continue;}
		ll A=fa[n]*fa[n+m-k]%mod;
		ll B=fa[n-k]*fa[n+m]%mod;
		B=qpow(B,mod-2,mod);//B转换为逆元
		ll ans=qpow(A*B%mod,q,mod);
		cout<<ans<<'\n';
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值