首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/882/A
来源:牛客网
涉及:排列组合数学
点击这里回到2019牛客暑期多校训练营解题—目录贴
题目如下:
说实话,这次出题人的脑洞真大
题目意思解释一下:有一个环,上面有n个点,分别是从0到n-1
从0号点出发,然后随意走,每落脚到一个地方就拿起此位置标记(刚开始每个点都有标记),然后求所有点走完后,最后落脚到m点的概率
ps:落脚到一个地方,表示暂时停止移动了,并不是路过一个地方。
注意:题目中还说明了按样例顺序最后落到每个点的概率。
也就是说,比如说样例输入吧,第一次n=1,最后落脚到0号点;第二次n=2,最后落脚到1号点,那么第二次落脚到1号点的概率就等于第一次n=1落脚到0号点的概率乘上n=2落脚到1号点的概率。
以此类推,第三次n=3,落脚到0号点的概率等于前两次的概率乘积乘上n=3落脚0号点的概率。
环上有n个点,从0号点出发走遍其他点,一共有 A n − 1 n − 1 A_{n-1}^{n-1} An−1n−1中可能的顺序,也就是其他n-1个点的排列数。
但是在这 A n − 1 n − 1 A_{n-1}^{n-1} An−1n−1中情况中,最后落脚点为m的情况有 A n − 2 n − 2 A_{n-2}^{n-2} An−2n−2,也就是另外n-2个点的排列数
1.如果
n
≥
1
n\ge 1
n≥1,从0号点出发,最后落脚点为
m
(
m
≠
0
)
m(m\ne 0)
m(m̸=0)的概率为
A
n
−
2
n
−
2
A
n
−
1
n
−
1
=
∏
i
=
1
n
−
2
i
∏
i
=
1
n
−
1
i
=
1
n
−
1
      
(
n
>
1
)
\frac {A_{n-2}^{n-2}}{A_{n-1}^{n-1}}=\frac {\prod_{i=1}^{n-2}i}{\prod_{i=1}^{n-1}i}=\frac 1{n-1}\;\;\;(n> 1)
An−1n−1An−2n−2=∏i=1n−1i∏i=1n−2i=n−11(n>1)
2.如果 n ≥ 1 n\ge 1 n≥1,从0号点出发,最后落脚点为0号点,那么概率一定为0,因为最后落脚点肯定不是0号点
3.如果 n = 1 , m = 0 n=1,m=0 n=1,m=0,从0号点出发,最后落脚点为0号点,概率一定为1,因为只有一个点,最后落脚点肯定是0号点
由于要按照所有场景发生的顺序来输出每个场景发生的概率,所以可以用一个前缀乘积来保存之前所有场景发生的概率,在乘上当前场景的概率,就是当前场景按顺序发生的概率了。
注意还要分数取模用快速幂
举个例子:
输入:
3
4 2
3 1
2 0
首先第一个场景一共4个点,落脚2号点的概率为 1 3 \frac 1{3} 31,所以此场景按顺序发生的概率为 1 3 ∗ 1 = 1 3 \frac 1{3}*1=\frac 1{3} 31∗1=31
第二个场景一共3个点,落脚1号点的概率为 1 2 \frac 1{2} 21,所以此场景按顺序发生的概率为 1 2 ∗ 1 3 = 1 6 \frac 1{2}*\frac 1{3}=\frac 1{6} 21∗31=61
第三个场景一共2个点,落脚0号点的概率为 0 0 0,所以此场景按顺序发生的概率为 0 ∗ 1 6 = 0 0*\frac 1{6}=0 0∗61=0
所以输出
1
3
\frac 1{3}
31
1
6
\frac 1{6}
61
0
0
0
代码如下:
#include <iostream>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int t,n,m;
ll ans=1;
ll qpow(ll x){//快速幂求分数取模逆元
ll pow=mod-2,sum=1;
while(pow){
if(pow%2==1) sum=sum*x%mod;
pow>>=1;
x=x*x%mod;
}
return sum;
}
int main(){
cin>>t;
while(t--){
scanf("%d%d",&n,&m);
//下面就是三种情况,注意ans保存前缀乘积
if(n==1 && m==0) ans=ans*1;
else if(m==0) ans=0;
else ans=ans*qpow(n-1)%mod;
printf("%lld\n",ans);
}
return 0;
}