A. On The Way to Lucky Plaza 概率 乘法逆元

A. On The Way to Lucky Plaza
time limit per test
1.0 s
memory limit per test
256 MB
input
standard input
output
standard output

Alaa is on her last day in Singapore, she wants to buy some presents to her family and friends. Alaa knows that the best present in the world will be a chocolate plate for each one of her family members and friends.

Alaa goes to Lucky Plaza shopping mall in Orchard road in order to find all chocolate she needs. Lucky Plaza is a big mall and have many shops that sell chocolate.

On the entrance of Lucky Plaza Alaa wondered if she wants to buy k chocolate plates, what is the probability that she will buy the kthchocolate plate from the nth shop she will visit, knowing that she can visit each shop at most one time. Also she can buy at most one chocolate plate from each shop, and the probability to do that is p. (This probability is the same for all shops in Lucky Plaza)

Alaa wants to finish her mission as soon as possible, so she starts visiting the shops, also she asked you to calculate the answer of her hard question. Can you?

Input

The first line contains three integers mnk and real number p (1  ≤  mnk  ≤  105) (0  ≤  p  ≤  1), where m is the number of shops that sell chocolate in Lucky Plaza, n is the number of shops Alaa will visit, k is the number of chocolate plates Alaa wants to buy, and p is the probability that Alaa will buy a chocolate plate from any shop that sell chocolate.

The probability p is given with exactly three digits after the decimal point

Output

On a single line print y, where y is the sought probability computed modulo 109 + 7.

The answer y is defined precisely as follows. Represent the probability that Alaa will buy the kth chocolate plate from the nth shop she will visit as an irreducible fraction p / q. The number y then must satisfy the modular equation y × q ≡ p (mod 109 + 7), and be between 0 and 109 + 6, inclusive. It can be shown that under the constraints of this problem such a number y always exists and is uniquely determined.

Examples
input
Copy
5 1 1 0.500
output
Copy
500000004
input
Copy
9 4 2 0.800
output
Copy
417600003
input
Copy
100 5 5 0.200
output
Copy
714240005
Note

In the first test case there are 5 shops that sell chocolate in Lucky Plaza, and Alaa wants to buy only 1 chocolate plate. In this case Alaa wants to know what is the probability that she will buy the 1st chocolate plate from the 1st shop she will visit. The probability is 1 / 2, and the answer is 500000004, since (500000004 * 2) % (109 + 7) = 1 % (109 + 7).

In the second test case there are 9 shops that sell chocolate in Lucky Plaza, and Alaa wants to buy only 2 chocolate plates. In this case Alaa wants to know what is the probability that she will buy the 2nd chocolate plate from the 4th shop she will visit. The probability is 48 / 625, and the answer is 417600003, since (417600003 * 625) % (109 + 7) = 48 % (109 + 7).

题解:博客

概率,线性递推求乘法逆元。
找到公式,就是求a*b%mod=C(n-1,k-1)*p^k*(1-p)^n-k%mod
转化成a=C(n-1,k-1)*p^k*(1-p)^n-k*inv(b)%mod;
先把p转化成一个long long的类型,注意a消除浮点误差,然后就是用杨辉三角求出组合数,最后就是求b的乘法逆元。

这里要注意的地方有不能直接用杨辉三角把组合数求出来,因为中间可能会超过mod,所以还是要一步一步的递推过去,然后再乘以一个乘法逆元。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100;
const int mod = 1e9 + 7;
const double eps = 1e-10;
ll inv[maxn];

int main()
{
	int m, n, k;
	double p;
	inv[1] = 1;
	for (int i = 2; i < maxn; i++) inv[i] = (mod - mod / i)*1ll*inv[mod%i]%mod;

	cin >> m >> n >> k >> p;
	if(m<n||k>n)
	{
		printf("0\n");
		return 0;
	}
	ll p1 = p * 1000 + eps,ans=1;
	//求组合数
	for (int i = 1; i <= k - 1; i++) ans = ans * (n - i) % mod;
	for (int i = 1; i <= k - 1; i++) ans = ans * inv[i] % mod;

	for (int i = 1; i <= k; i++) ans = ans * p1%mod;
	for (int i = 1; i <= n - k; i++) ans = ans * (1000 - p1) % mod;

	for (int i = 1; i <= n; i++) ans = ans * inv[1000] % mod;

	printf("%lld\n", ans);
	return 0;
}

  

然后介绍一下三种求乘法逆元的方法:

乘法逆元
对于两个数a,p若gcd(a,p)=1则一定存在另一个数b,使得ab≡1(modp),并称此时的b为a关于1模p的乘法逆元。我们记此时的b为inv(a)或 。

举个例子:5×3≡1(mod14),我们称此时的3为5关于1模14的乘法逆元。

如何求乘法逆元?
方法一:费马小定理
费马小定理:当有两数a,p满足gcd(a,p)=1时,则有ap≡a(modp)。
变一下形:a* ≡1(modp)。是不是和上面的乘法逆元的定义是相似的?
所以,我们可以使用快速幂求出 ,即求出a的逆元。
long long PowMod(long long a,int b) {
    long long ret=1;
    while(b) {
        if(b&1)ret=ret*a%Mod;           //b&1 在这里和b%2 的含义一样
        a=a*a%Mod;
        b>>=1;
    }
    return ret;
}

方法二:扩展欧几里得算法
由定义可知:ab≡1(modp),这个式子等价于已知a,p求一个二元一次不定方程ab=kp+1,移一下项得:ab−kp=1。这东西不是扩展欧几里得算法?
long long extend_gcd(long long a,long long b,long long &x,long long &y) {
    if(a==0&&b==0)
        return -1;
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    long long d=extend_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
long long mod_reverse(long long a,long long n) {
    long long x,y,d=extend_gcd(a,n,x,y);
    if(d==1) {
        if(x%n<=0)return x%n+n;
        else return x%n;
    } else return -1;
}

方法三:递推计算阶乘的逆元
当我们要计算一大串连续的阶乘的逆元时,采用费马小定理或扩展欧几里得算法就有可能超时,所以我们必须采用一个更快的算法。
令 ,则可得:
 (mod p)
我们将(i+1)(i+1)乘过去,则有:
 (mod p)
自然我们就得出递推式。
乘法逆元的作用?
我们由费马小定理可得:a* ≡1(modp)。
所以:
 ≡ ≡1/a(modp)
我们又知道模运算的乘法结合律:
a/b≡b* ≡b* (modp)
所以我们可以知道:a除以一个数模p,等于a乘这个数的乘法逆元模p。
f[0]=1;
for(int i=1;i<=N;i++)
    f[i]=f[i-1]*i%Mod;
inv[0]=1;
inv[N]=PowMod(f[N],Mod-2);
for(int i=N-1;i>0;i--)
inv[i]=inv[i+1]*(i+1)%Mod;

下为几种方法求逆元的代码实现
int inv[1000010];
LL ksm(LL a,LL b,LL mod)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
} 
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    LL GCD=exgcd(b,a%b,x,y);
    LL tmp=x;
    x=y;
    y=tmp-a/b*y;
    return GCD;
}
LL inv1(LL a,LL mod)//扩展欧几里得求逆元 
{
    LL x,y;
    LL d=exgcd(a,mod,x,y);
    if(d==1) return (x%mod+mod)%mod;
    return -1;
}
LL inv2(LL a,LL mod)//费马小定理
{
    return ksm(a,mod-2,mod);
} 
void inv3(LL mod)//线性递推求逆元 
{
    inv[1]=1;
    for(int i=2;i<=mod-1;i++)
    {
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        cout<<inv[i]<<" ";
    }
}
int main()
{
    LL n,mod;
    while(cin>>n>>mod)
    {
        cout<<inv1(n,mod)<<" "<<inv2(n,mod)<<endl;
        inv3(mod);
    }
} 

  

然后再加一个杨辉三角求组合数:

组合数:
按照杨辉三角(递推):
memset(c,0,sizeof(c));
for (int i=0; i<=n; i++){
	c[i][0]=1;
	for (int j=1; j<=i; j++)
	 c[i][j]=c[i-1][j-1]+c[i-1][j];
}
优化形式---利用 :
c[0]=1;
for (int i=1; i<=n; i++) c[i]=c[i-1]*(n-i+1)/i;

  

猜你喜欢

转载自www.cnblogs.com/EchoZQN/p/10659772.html
way