题目
为了知道自己的排名,小C使用了系统中的“好友伴学”功能。
每次,系统会在除了小C之外的所有考生中随机抽取一名,然后返回Ta的排名比小C高还是低。
这次考试有n(2<=n<=1e11)个人参加,可以认为小C的排名是一个在[1,n]内等概率随机的整数。
小C总共使用的m(0<=m<=5e3)次“好友伴学”功能,却没有一次抽中排名比自己高的人。
请问小C在这次考试中的期望排名是多少,
若答案为,输出
思路来源
题解
【题解】牛客挑战赛36_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网
求自然数幂和
【学习笔记】自然数幂和-CSDN博客 第二类斯特林数、伯努利数、插值等
自然数幂和-CSDN博客 扰动法递推
题解
令pi表示排名为第n-i时,m次没抽中的概率
由于排名只可能为[1,n],则
令A为m次没抽中,B为排名为i
根据贝叶斯公式,,
,根据全概率公式打开,即每一种排名下对应的概率之和,
,
,
为所求,
以上各式,代入
且注意到,m次没抽中时排名为i的概率为P时,给答案带来的贡献是P*(n-i)
所求式,即,化简有
由于题目只要求效率的算法,可以直接递推求解自然数幂和。
法一
粘个公式就跑
1/(j+1)逆元搞搞,{n,j}为第二类斯特林数S(n,j),
(m+1)的j+1次下降幂,就是从(m+1)*m*(m-1)*...往下乘,共乘j+1项
代码一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e3+5,mod=998244353;
ll n,m,s[N][N],inv[N];
ll modpow(ll x,ll n,ll mod){
ll res=1;
for(;n;n>>=1,x=x*x%mod){
if(n&1)res=res*x%mod;
}
return res;
}
//i从0到n i的m次方之和
ll cal(ll n,ll m){
ll ans=0,now=1;
for(int j=0;j<=m;++j){
now=(n+1-j)%mod*now%mod;
ans=(ans+1ll*inv[j+1]*s[m][j]%mod*now%mod)%mod;
}
return ans;
}
int main(){
scanf("%lld%lld",&n,&m);
//第二类斯特林数
s[0][0]=1;
for(int i=1;i<N;++i){
for(int j=0;j<=i;++j){
s[i][j]=((j-1>=0?s[i-1][j-1]:0)+1ll*j*s[i-1][j]%mod)%mod;
}
}
//逆元
inv[1]=1;
for(int i=2;i<N;++i){
inv[i]=1ll*(mod-inv[mod%i])*(mod/i)%mod;
}
printf("%lld\n",(n-(cal(n-1,m+1)*modpow(cal(n-1,m),mod-2,mod)%mod)+mod)%mod);
return 0;
}
法二
扰动法递推,就是加一项减一项,
对Sk(n)这么展开,可以求Sk-1(n)的值
设,有
代码二
注意,0的0次方没意义,s[0]的值,只是用来后续s递推,
所以,m=0的询问,结合原题意义,要特判
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5555,mod=998244353;
ll n,m,c[N][N],inv[N],s[N],ans,res;
ll modpow(ll x,ll n,ll mod){
ll res=1;
for(;n;n>>=1,x=x*x%mod){
if(n&1)res=res*x%mod;
}
return res;
}
//求s[i]=(i=0到n i的m次方)之和
void gao(ll n,ll m){
s[0]=n%mod;
ll now=(n+1)%mod;
for(int i=1;i<=m;++i){
now=(n+1)%mod*now%mod;
s[i]=(now-1+mod)%mod;
for(int j=0;j<=i-1;++j){
s[i]=(s[i]-1ll*c[i+1][j]*s[j]%mod+mod)%mod;
}
s[i]=1ll*s[i]*inv[i+1]%mod;
}
}
int main(){
scanf("%lld%lld",&n,&m);
//第二类斯特林数
c[0][0]=1;
for(int i=1;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])%mod;
}
}
inv[1]=1;
for(int i=2;i<N;++i){
inv[i]=1ll*(mod-inv[mod%i])*(mod/i)%mod;
}
gao(n-1,m+1);
if(m==0)printf("%lld\n",(n-(s[1]*modpow(n%mod,mod-2,mod)%mod)+mod)%mod);
else printf("%lld\n",(n-(s[m+1]*modpow(s[m],mod-2,mod)%mod)+mod)%mod);
return 0;
}