CF1096E The Top Scorer

题目传送门:CF1096E

洛谷入口

题目大意:

\(p个人,每个人有得分a_i\)
\(总得分∑a_i=s\)
\(第一个人得分a_1≥r\)
\(得分最高的人可以获胜,如果多个人得分最高,则等概率随机其中一个人获胜\)
\(问第一个人获胜的概率(运算皆对998244353取模)\)

数据范围

\(\circ\) \(1\le p\le100\)
\(\circ\) \(0\le r\le s\le5000\)

题解

这题算概率,就是获胜方案数除以总情况数
那要求获胜方案数就可以试着去\(DP\)
首先就先枚举获胜者的分数,假设为\(i\)
接下来再假设有\(j\)个人都是这个分数\(i\)(包含此获胜者)
那么剩余人还有\(p-j\)
剩余分数还有\(s-i\times j\)
而且这些人分数都不到\(i\)
剩余的人分一些分简单,但如何让每个人得分都不超过\(i\)呢?
其实可以用容斥的思想做一下
我们先考虑至少\(0\)人超过要求的方案数
再减去至少\(1\)人的方案数
会发现减多了,于是再加回至少\(2\)人的方案数
…………………………
最后就加上\((-1)^i\times\)至少p-j人方案数
那对于至少\(x\)人,要求其超过\(lim-1\)的求法:
先给这\(x\)人每人分个\(lim\)的分数
然后再按照隔板法来分(要选其中元素可为\(0\)的方法)
这样就可以算出一个通式:
\((-1)^x\times C_p^x \times C^{p-1}_{s-lim\times x+p-1}\)
\(i\)再按照\(0\)\((p-j)\)枚举即可解决对于问题\(Q(总个数s,总人数p,限制lim)\)的方案数
那把这个套进去大的轮廓会得到答案求法:
\(\sum\limits^{s}_{i=r}\sum\limits^{p}_{j=1}Q(s-i\times j,p-j,i)\times 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)\times 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;
}

编辑不易,支持一下吧,关注,点赞,评论都好!

THE END

posted @ 2020-03-30 11:40  Realityang  阅读(92)  评论(0编辑  收藏  举报