题目传送门:CF1096E
洛谷入口
题目大意:
(p个人,每个人有得分a_i)
(总得分∑a_i=s)
(第一个人得分a_1≥r)
(得分最高的人可以获胜,如果多个人得分最高,则等概率随机其中一个人获胜)
(问第一个人获胜的概率(运算皆对998244353取模))
数据范围
(circ) (1le ple100)
(circ) (0le rle sle5000)
题解
这题算概率,就是获胜方案数除以总情况数
那要求获胜方案数就可以试着去(DP)
首先就先枚举获胜者的分数,假设为(i)
接下来再假设有(j)个人都是这个分数(i)(包含此获胜者)
那么剩余人还有(p-j)个
剩余分数还有(s-i imes j)
而且这些人分数都不到(i)
剩余的人分一些分简单,但如何让每个人得分都不超过(i)呢?
其实可以用容斥的思想做一下
我们先考虑至少(0)人超过要求的方案数
再减去至少(1)人的方案数
会发现减多了,于是再加回至少(2)人的方案数
…………………………
最后就加上((-1)^i imes)至少p-j人方案数
那对于至少(x)人,要求其超过(lim-1)的求法:
先给这(x)人每人分个(lim)的分数
然后再按照隔板法来分(要选其中元素可为(0)的方法)
这样就可以算出一个通式:
((-1)^x imes C_p^x imes C^{p-1}_{s-lim imes x+p-1})
那(i)再按照(0)到((p-j))枚举即可解决对于问题(Q(总个数s,总人数p,限制lim))的方案数
那把这个套进去大的轮廓会得到答案求法:
(sumlimits^{s}_{i=r}sumlimits^{p}_{j=1}Q(s-i imes j,p-j,i) imes C^{j-1}_{p-1}/j)
其实理解到这,总情况求法肯定都没问题了吧
就是(C^{p-1}_{s-r+p-1})
那么解题步骤就这些
式子没啥进一步推的,这个复杂度就可以过了
注意事项
建议把所有范围内的组合数都求一遍
①.组合数递推式是一个公式(其实可以用实际问题的方式轻松证明)
(C^{j}_{i}=C^{j}_{i-1}+C^{j-1}_{i-1})
注意初始化(C^0_i都为1)
②.逆元的线性求法有一个公式
(inv_i=mod-(mod/i) imes inv_{mod\%i}\%mod)
初始化的话记得加个(inv_1=1)
③.最后除以总情况时最好用个费马小定理解决
(说到费马小定理就别忘了带上快速幂)
好了就这些了好像挺多的,下面上代码!↓↓↓
AC代码
#include<bits/stdc++.h>
#define maxn 5200
#define int long long//懒的表现
using namespace std;
int mod=998244353,inv[maxn],c[maxn+10][maxn+10],p,s,r,ans;//懒*2
void init(){//初始化inv&&c
inv[1]=1;
for(int i=2;i<=p;i++)inv[i]=mod-(mod/i)*inv[mod%i]%mod;
c[0][0]=1;
for(int i=1;i<=maxn;i++){
c[0][i]=1;
for(int j=1;j<=i;j++)c[j][i]=(c[j][i-1]+c[j-1][i-1])%mod;
}
}
int ksm(int x,int y){
int tot=1,tmp=x;
while(y){
if(y&1)tot=tot*tmp%mod;
tmp=tmp*tmp%mod;
y>>=1;
}
return tot;
}
int C(int a,int b){//为了谨慎而存在
if(a>b||a<0||b<0)return 0;
return c[a][b];
}
int jj(int sum,int x,int lim){
if(sum==0)return 1;
if(sum<0)return 0;
int tot=0;
for(int i=0;i<=x;i++)tot=(tot+((i&1)?998244352:1)*c[i][x]%mod*C(x-1,sum-i*lim+x-1))%mod;
return tot;
}
signed main(){
cin>>p>>s>>r;
init();
for(int i=r;i<=s;i++){
for(int j=1;j<=p;j++){
if((p-j)*(i-1)+i*j<s)continue;
ans=(ans+jj(s-i*j,p-j,i)*C(j-1,p-1)%mod*inv[j])%mod;
}
}
cout<<ans*ksm(c[p-1][s-r+p-1],mod-2)%mod;
}