CF1096E 题解
题目大意
小明在打比赛,包括小明自己一共有 \(p\) 名选手参赛,每个人的得分是一个非负整数。最后的冠军是得分最高的人,如果得分最高的人有多个,就等概率从这些人中选一个当冠军。
现在小明已知了自己的得分大于等于 \(r\),所有选手的得分和为 \(s\)。求小明获胜的概率,结果对 \(998244353\) 取模。
\(Solution:\)
我们只需要保证小明为\(TopScorer\)即可,即保证剩下的人的分数都\(\leq\)小明的分数,\(ans\)即为小明为\(TopScorer\)的方案数除以总方案数
因为这是DP专题里面的题,我们令\(f_{s,p,m}\)表示\(p\)名选手总得分为\(s\),每名选手分数不超过\(m\)的方案数,显然有:
\(f_{s,p,m}=\sum\limits_{i=0}^{m}{f_{s-i,p-1,m}}\),可以维护一个前缀和,但是这是\(O(srp)\)的,显然\(TLE\)
但是这启发我们我们可以考虑单独计算某个\(dp\)值(答案用到的\(dp\)状态不多)为什么我就没有被启发到
那么\(f_{s,p,m}=\sum\limits_{i=0}^{p}{(-1)^{i} \times C_p^i \times C_{s+p-1-i(m+1)}^{p-1}}\)
问题来了,如何理解这个式子?(天知道我为了理解它花了多长时间)
第一种理解方式:容斥
遇到有关最大值的问题,可以转化为“不超过”
在此题中,即为其他人的分数都不超过小明的分数
即计算所有情况-至少一个人超过\(m+\)至少两个人超过\(m-\)至少三个人超过\(m\)\(+...\)(奇加偶减),所以容斥系数即为\((-1)^i\)
再考虑\(C_p^i \times C_{s+p-1-i(m+1)}^{p-1}\),首先在\(p\)个人中选出\(i\)个人,即为\(C_p^i\),再给每个人分配\((m+1)\)的分数(意思即保证这些人的分数都大于\(m\)),然后就是允许有空盒子的隔板法了
第二种理解方式:二项式反演
二项式反演就是这样的很对称一个式子:
\(f_{n}=\sum_{i=0}^{n}(-1)^{i} \times C_{n}^{i} \times g_{i} \Longleftrightarrow g_{n}=\sum_{i=0}^{n}(-1)^{i} \times C_{n}^{i} \times f_{i}\)
一个很对称的常见表达为:\(f_{n}=\sum_{i=0}^{n} C_{n}^{i} \times g_{i} \Longleftrightarrow g_{n}=\sum_{i=0}^{n}(-1)^{n-i} \times C_{n}^{i} \times f_{i}\)
具体证明可以看看这篇博客
那么我们把它套到我们的式子中,设\(A(x)\)表示\(\leq x\)个人不超过\(m\)的方案数,\(B(x)\)表示恰好有\(x\)个人不超过\(m\)的方案数,显然有:
\(A(n)=\sum\limits_{i=0}^nC_n^i \times B(i)\),由二项式反演:
\(B(n)=\sum\limits_{i=0}^{n}{(-1)^{n-i} \times C_n^i \times A(i)}\),将 \(i\) 全部换成 \(n-i\)(这么换相当于把原来的正序\(\sum\)改成了倒序,不会影响答案),有:
\(B(n)=\sum\limits_{i=0}^{n}{(-1)^{i} \times C_n^i \times A(n-i)}\),(其中 \(C_{n}^{n-i}=C_{n}^i\),所以不变)因为有\(n-i\)个人不超过\(m\) \(\Longleftrightarrow\) 有\(i\)个人超过\(m\),那么给每个人分配\(m+1\)分,再使用隔板法,有:
\(B(p)=\sum\limits_{i=0}^{p}{(-1)^i \times C_p^i \times C_{s+p-1-i(m+1)}^{p-1}}\),这样就得到了同样的结论
回归正题,我们得到了\(f_{s,p,m}\)的表达式后,我们尝试用它来表示\(answer\),那么:
问题又双叒叕来了,如何解释这个式子?
外层循环\(t\)枚举的是小明的分数,即所有人的分数\(\leq t\);
\(q\)枚举的是有多少同分的最高分,\(C_{p-1}^{q-1}\) 描述的是从\(p-1\)个人(\(-1\)意即除去小明)中选出 \(q-1\)个人,\(\frac{1}{q}\)即题目中同分的人均分成为\(TopScorer\)的概率;
\(f_{s-qt, p-q, t-1}\)描述的是除去最高分的人,剩下的方案数;
最后分母 \(C_{s-r+p-1}^{p-1}\) 描述的是总的方案数。
呼~终于讲完了,上代码
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
namespace my_std
{
typedef long long ll;
typedef double db;
#define pf printf
#define pc putchar
#define fr(i,x,y) for(register ll i=(x);i<=(y);i++)
#define pfr(i,x,y) for(register ll i=(x);i>=(y);i--)
#define go(x) for(ll i=head[u];i;i=e[i].nxt)
#define enter pc('\n')
#define space pc(' ')
#define fir first
#define sec second
#define MP make_pair
const ll inf=0x3f3f3f3f;
const ll inff=1e15;
inline ll read()
{
ll sum=0,f=1;
char ch=0;
while(!isdigit(ch))
{
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch))
{
sum=(sum<<1)+(sum<<3)+(ch^48);
ch=getchar();
}
return sum*f;
}
inline void write(ll x)
{
if(x<0)
{
x=-x;
pc('-');
}
if(x>9) write(x/10);
pc(x%10+'0');
}
inline void writeln(ll x)
{
write(x);
enter;
}
inline void writesp(ll x)
{
write(x);
space;
}
}
using namespace my_std;
const ll N=5500,mod=998244353;
ll p,r,s,mul[N],inv[N],c[N][N],ans;
inline ll ksmod(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
{
ans=(ans*a)%mod;
}
a=(a*a)%mod;
b>>=1;
}
return ans;
}
inline ll C(ll n,ll m)
{
if(m>n||m<0||n<0) return 0;
ll res=inv[m]*inv[n-m]%mod;
res=res*mul[n]%mod;
return res;
}
inline void init()
{
mul[0]=inv[0]=1;
for(ll i=1;i<=p+s;i++) mul[i]=(mul[i-1]*i)%mod;
inv[p+s]=ksmod(mul[p+s],mod-2);
for(ll i=p+s-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll f(ll s,ll p,ll m)
{
if(s==0&&p==0) return 1;
ll ans=0,tmp=1;
fr(i,0,p)
{
if(s+p-1-i*(m+1)<0) break;
ans=(ans+tmp*C(p,i)%mod*C(s+p-1-i*(m+1),p-1)%mod+mod)%mod;
tmp*=-1;
}
return ans;
}
int main(void)
{
p=read(),s=read(),r=read();
init();
fr(t,r,s) fr(q,1,p) ans=(ans+(C(p-1,q-1)*ksmod(q,mod-2))%mod*f(s-q*t,p-q,t-1))%mod;
ans=(ans*ksmod(C(s-r+p-1,p-1),mod-2))%mod;
writeln(ans);
return 0;
}