这是一道关于概率的好题,主要考思维。老师说的
分析
首先从题目可以看出,$n,m$ 的数据量是 $10^6$ 级别的,所以我们只能考虑 $O(nlogn),O(n) $ 和 $ O(1)$ 级别的代码,无疑将 $DP$ 的可能性排除,因为题目中又要考虑到概率的问题,于是乎,我们便可以采用数学的方法。
方法概括
借鉴了一些观点,我们可以将这一条链变成一个环,即用一个节点连接两端,这样就可以将 $n$ 个节点变为 $n+1$ 个而我们只需要考虑多加上的这个节点有没有被坐到即可。
首先我们要判断有以下两个概率
1.一个人向这一个方向走的概率 $P1$ 为 $ (2(n+1))^m$
2.每个座位空着的概率($n+1$ 个座位被坐了 $m$ 个) 即 $P2$ 为 $\frac{n+1-m}{n+1}$
最后的答案即为 $P1$ 与 $P2$ 的乘积(因为考虑第 $n+1$ 个点空着的概率是 $P2$ 然后所有人走到它这里的概率为 $P1$ 所以相乘就是答案)
code
#include<bits/stdc++.h>
#define int long long
const int mod=1e9+7;
using namespace std;
inline int read(){
int t=1,x=0;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') t=-1;
for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return t*x;
}//快读,纯纯个人习惯,与本题无关
int n,m;
int _pow(int x,int y){
int res=1;
while(y){
if(y&1) res=(res*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return res;
}//快速幂板子
/*
主要考虑本题要求取模,所以直接pow的话可能会导致直接爆掉,而用快速幂则可以保证每乘一次取模一次,保证不会爆,而且可以用来求逆元。
*/
signed main(){
n=read(),m=read();
//int p1=(n+1-m)/(n+1); 错误示范,这样会丢失很多精度,导致答案偏差过大
int p1=(n+1-m)*_pow(n+1,mod-2)%mod;
int p2=_pow(2*(n+1),m);
cout<<p1*p2%mod;
return 0;
}
end
为什么除法要用逆元?
在除法运算中不可以对分子分母分别取模来进行计算,当 $a$ 和 $b$ 足够大时如果强行分别取模的话会导致精度缺失较大,结果就不准确了。
逆元可以用各种方法解决,这里只提供了快速幂的解法,其他的方法可以再学习。