CF1096E The Top Scorer

CF1096E The Top Scorer

总方案数,直接插板,为 (inom{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{inom{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}inom{n}{i}g(i))

二项式反演的公式拿出来:

[f(n)=sum_{i=1}^{n}inom{n}{i}g(i)Leftrightarrow g(n)=sum_{i=1}^{n}(-1)^{n-i}inom{n}{i}f(i) ]

发现

[g(x)=sum_{i=1}^{x}(-1)^{n-i}inom{n}{i}h(i) ]

可得

[g(n)=sum_{i=1}^{n}(-1)^{n-i}inom{n}{i}h(i) ]

发现这个 (h(i)) 非常好算

直接先让 (n-i) 个状态不合法掉,其余随机分配就好了。

还是插板。设 (p(n,m)=inom{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
",ans);
	return 0;
}
路漫漫其修远兮,吾将上下而求索
原文地址:https://www.cnblogs.com/zzctommy/p/13849524.html