T7 The Top Scorer (CF1096E)
T7 The Top Scorer (CF1096E)
CF传送门
洛谷传送门
题意简述
小明在打比赛,包括小明自己共有
现在小明已知自己的得分大于等于
数据范围
题解
思路
首先来暴力dp
涉及到最大值,因此可以让所有人都不超过某个值
设
状态转移方程很好写了,考虑新加入的是得分为i的选手
怎么计算答案呢?
考虑到有多个人并列第一的情况,不妨枚举q人并列第一并且小明得t分,计算情况总数
当然这q个人里必须包含小明
情况总数
别忘了答案是求概率,还得除以总数
总数怎么计算呢?
其实就是个数学问题,插板法
假设每个人的分数为
那么就是求
转化成正整数
即求
就是插板法,在
即共
写了这么多,累死我了 该出答案了吧?
木有
会发现这是
那么接下来的事情就与dp无关了
看看
所以能不能不经过递推,直接把单个dp算出来呢?
可以。
然后来考虑至少有
首先要选出这
这时候又可以来用插板法了,用
记
刚开始选出来的
最后来容斥,
把
还有ans的算法
时间复杂度最大
做法
对于每一个要求的dp值,按照公式计算即可
组合数逆元啥的很基础了
还需要解释吗?
注意事项
· 注意特判,尤其注意
· long long...
AC代码
#include<bits/stdc++.h>
using namespace std;
const long long P=110,N=5005;
long long mod=998244353;
long long mul[P+N],inv[P+N];
long long p,s,r,ans=0;
long long C(long long m,long long n){
if(m<0 || n<0 || m-n<0) return 0;//必要的,因为会有负数
if(m*n==0) return 1;//特判
return mul[m]*inv[n]%mod*inv[m-n]%mod;
}
long long ksm(long long a,long long x){
long long sum=1;
while(x){
if(x&1) sum=sum*a%mod;
a=a*a%mod,x>>=1;
}
return sum;
}
long long dp(long long s,long long p,long long m){
if(s==0 && p==0) return 1;//奇怪的特判
long long tot=0;
for(long long i=0;i<=p;i++){
long long tmp=C(p,i)*C(s+p-1-i*(m+1),p-1)%mod;
tot= (i%2)?(tot-tmp+mod)%mod:(tot+tmp)%mod;
}
return tot;
}
int main(){
scanf("%lld%lld%lld",&p,&s,&r);
mul[0]=inv[0]=1;
for(long long i=1;i<=p+s;i++) mul[i]=mul[i-1]*i%mod;
inv[p+s]=ksm(mul[p+s],mod-2);
for(long long i=p+s-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
//预处理阶乘和逆元
for(long long t=r;t<=s;t++)
for(long long q=1;q<=p;q++)
ans=(ans+C(p-1,q-1)*ksm(q,mod-2)%mod*dp(s-q*t,p-q,t-1)%mod)%mod;
//公式计算答案
ans=(ans*ksm(C(s-r+p-1,p-1),mod-2))%mod;//最后要除以总数
printf("%lld",ans);
}