CF1096E The Top Scorer
总方案数,直接插板,为 \(\binom{s-r+p-1}{p-1}\) ,意思是先分 \(r\) 分给小明,然后再分为 \(p-1\) 个非负整数和
接下去算合法的方案数
枚举小明的最高分为 \(i\) ,令 \(j\) 个人与小明同分,设 \(f(x,y,z)\) 表示 \(x\) 个数,和为 \(y\) ,最大值不超过 \(z\) 的方案数,那么合法的方案数等于
\[\sum_{i=r}^{s}\sum_{j=0}^{p}\dfrac{\binom{p-1}{j}f(p-j-1,s-(j+1)*i,i-1)}{j+1}
\]
考虑如何求 \(f\)
我看了题解才知道,这东西触及到我的知识盲区了,怎么一不小心做到这种题
考虑设所求答案为 \(g(n)\) ,\(g(i)\) 表示恰有 \(n-i\) 个不合法状态的情况数。
再搞一个函数 \(h(i)\) 表示至少 \(n-i\) 个不合法状态的方案数
非常显然 \(h(i)=\sum_{j=1}^{i}\binom{n}{i}g(i)\)
二项式反演的公式拿出来:
\[f(n)=\sum_{i=1}^{n}\binom{n}{i}g(i)\Leftrightarrow g(n)=\sum_{i=1}^{n}(-1)^{n-i}\binom{n}{i}f(i)
\]
发现
\[g(x)=\sum_{i=1}^{x}(-1)^{n-i}\binom{n}{i}h(i)
\]
可得
\[g(n)=\sum_{i=1}^{n}(-1)^{n-i}\binom{n}{i}h(i)
\]
发现这个 \(h(i)\) 非常好算
直接先让 \(n-i\) 个状态不合法掉,其余随机分配就好了。
还是插板。设 \(p(n,m)=\binom{n+m-1}{m-1}\) 表示 \(m\) 个非负整数加起来等于 \(n\) 的方案数
\[h(i)=p(x-(i+1)*(z+1),x)
\]
带回去就好了
有两个细节:
-
组合数预处理不能只处理到 \(5000\) ,最后答案还要乘的那个分母要用到 \(5100\)
-
\(f(0,0,z)=1\) ,注意特判
#include<bits/stdc++.h>
typedef long long LL;
typedef double db;
#define x first
#define y second
#define sz(v) (int)v.size()
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
return f?x:-x;
}
const int mod=998244353;
const int N=10005;
int p,s,r,ans;
int jc[N],jv[N],inv[N];
int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
void init(const int&N){
jc[0]=1;
for(int i=1;i<=N;++i)jc[i]=1ll*jc[i-1]*i%mod;
jv[N]=qpow(jc[N],mod-2);
for(int i=N-1;i>=0;--i)jv[i]=1ll*jv[i+1]*(i+1)%mod;
inv[1]=1;
for(int i=2;i<=N;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
int C(int n,int m){return 1ll*jc[n]*jv[m]%mod*jv[n-m]%mod;}
int g(int n,int m){return C(n+m-1,m-1);}//sum of m non-neg integers=n
int f(int x,int y,int z){//number=x,sum=y,limit=z
if(!x&&!y)return 1;
int res=0,flg=(x&1)?-1:1;
for(int i=0;i<=x;++i,flg*=-1)
if(y-(z+1)*(x-i)>=0)res=(res+1ll*flg*C(x,i)*g(y-(z+1)*(x-i),x)%mod)%mod;
return res;
}
signed main(){
init(6000);
p=read(),s=read(),r=read();
for(int i=r;i<=s;++i){
for(int j=0;(j+1)*i<=s&&j<p;++j){
ans=(ans+1ll*C(p-1,j)*f(p-j-1,s-(j+1)*i,i-1)%mod*inv[j+1]%mod)%mod;
}
}
ans=1ll*ans*qpow(C(s-r+p-1,p-1),mod-2)%mod;
printf("%d\n",ans);
return 0;
}
路漫漫其修远兮,吾将上下而求索