The 2021 CCPC Guangzhou A. Math Ball

A. Math Ball

题意

给定n个不同的小球,每种小球都有无穷多个,第i个小球有一个权值 c i c_{i} ci,现在你要从中选出不超过W个小球,定义其选择的权值为 ∑ k 1 + k 2 . . . . k n < = W ∏ i = 1 n k i c i \sum_{k_{1}+k_{2}....k_{n}<=W}\prod_{i=1}^{n}k_{i}^{c_{i}} k1+k2....kn<=Wi=1nkici,问所有选择方案的权值之和.

解析

考虑 f i ( x ) = ∑ j = 0 ∞ j c i x j f_{i}(x)=\sum_{j=0}^{\infin}j^{c_{i}}x^{j} fi(x)=j=0jcixj,则原式变为 ∑ i = 0 W [ x i ] ∏ j = 1 n f j ( x ) \sum_{i=0}^{W}[x^{i}]\prod_{j=1}^{n}f_{j}(x) i=0W[xi]j=1nfj(x),乘上个 1 1 − x \frac{1}{1-x} 1x1求个前缀和则变为 [ x W ] 1 1 − x ∏ j = 1 n f j ( x ) [x^{W}]\frac{1}{1-x}\prod_{j=1}^{n}f_{j}(x) [xW]1x1j=1nfj(x),现在我们考虑如何处理 f i ( x ) f_{i}(x) fi(x)

考虑将自然数幂和展开 i k = ∑ j = 0 i { k j } j ! ( i j ) i^{k}=\sum_{j=0}^{i}{k\brace j}j!{\binom{i}{j}} ik=j=0i{jk}j!(ji)

f i ( x ) = ∑ j = 0 ∞ x j ∑ k = 0 j { c i k } k ! ( j k ) = ∑ k = 0 ∞ { c i k } k ! ∑ j = k ∞ x j ( j k ) f_{i}(x)=\sum_{j=0}^{\infin}x^{j}\sum_{k=0}^{j}{c_{i}\brace k}k!{\binom{j}{k}}=\sum_{k=0}^{\infin}{c_{i}\brace k}k!\sum_{j=k}^{\infin}x^{j}{\binom{j}{k}} fi(x)=j=0xjk=0j{kci}k!(kj)=k=0{kci}k!j=kxj(kj)

而我们知道 ∑ j = k ∞ x j ( j k ) = x k ( 1 − x ) k + 1 \sum_{j=k}^{\infin}x^{j}{\binom{j}{k}}=\frac{x^{k}}{(1-x)^{k+1}} j=kxj(kj)=(1x)k+1xk

所以原式可以转化为 f i ( x ) = ∑ k = 0 c i { c i k } k ! x k ( 1 − x ) k + 1 f_{i}(x)=\sum_{k=0}^{c_{i}}{c_{i}\brace k}k!\frac{x^{k}}{(1-x)^{k+1}} fi(x)=k=0ci{kci}k!(1x)k+1xk

我们发现由于 ∑ i = 1 n c i < = 1 0 5 \sum_{i=1}^{n}c_{i}<=10^{5} i=1nci<=105,所以 { c i k } {c_{i}\brace k} {kci}可以直接第二类斯特林数行就行了

我们设 g ( x ) = x 1 − x g(x)=\frac{x}{1-x} g(x)=1xx,则 f i ( x ) = 1 1 − x ∑ k = 0 c i { c i k } k ! g ( x ) k f_{i}(x)= \frac{1}{1-x}\sum_{k=0}^{c_{i}}{c_{i}\brace k}k!g(x)^{k} fi(x)=1x1k=0ci{kci}k!g(x)k

所以我们要求的就变成了 [ x W ] 1 ( 1 − x ) n + 1 ∑ k = 0 c 1 + c 2 . . . . c n A k g ( x ) k [x^{W}]\frac{1}{(1-x)^{n+1}}\sum_{k=0}^{c_{1}+c_{2}....c_{n}}A_{k}g(x)^{k} [xW](1x)n+11k=0c1+c2....cnAkg(x)k

我们考虑对每一个 k k k,算出 [ x W ] 1 ( 1 − x ) n + 1 g ( x ) k [x^{W}]\frac{1}{(1-x)^{n+1}}g(x)^{k} [xW](1x)n+11g(x)k,即 [ x W − k ] 1 ( 1 − x ) n + k + 1 [x^{W-k}]\frac{1}{(1-x)^{n+k+1}} [xWk](1x)n+k+11,直接广义二项式定理展开,答案为 ( W + n W − k ) \binom{W+n}{W-k} (WkW+n) ( W + n n + k ) \binom{W+n}{n+k} (n+kW+n)

补充:如何计算第二类斯特林数行?

{ n m } n\brace m {mn}= 1 m ! ∑ i = 0 m ( m i ) ( − 1 ) i ( m − i ) n \frac{1}{m!}\sum_{i=0}^{m}\binom{m}{i}(-1)^{i}(m-i)^{n} m!1i=0m(im)(1)i(mi)n

#include<bits/stdc++.h>
#define ll long long
#define mk(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define cs const
using namespace std;
cs int g=3;
const int N=8e5+10;
const int mod=998244353;
typedef vector<int> poly;
int rev[N],inv[N],fac[N],invf[N];
int n,m,len,d,x,k;
poly a;
int mul(int x,int y){return (ll)x*y%mod;}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y>=0?x-y:x-y+mod;}
int ksm(int x,int y){
	ll ans=1;
	for (;y;y>>=1,x=mul(x,x)) if (y&1) ans=mul(ans,x);
	return ans;
}
int read(){
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9'){if (c=='-')f=-1;c=getchar();}
	while (c>='0'&&c<='9'){x=add(mul(x,10),c-'0');c=getchar();}
	return x; 
}
int init(int x){
	int len=1,d=0;
	while (len<x) {len<<=1;d++;}
	for (int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(d-1));
	return len;
}
int BSGS(int X,int Y){
	int now=floor(sqrt(mod));
	int s=ksm(X,now);
	map<int,int>mp;
	int t=1;
	int sum=Y;
	for (int i=0;i<now;i++){
		mp[sum]=i; sum=mul(sum,X);
	}
	int s1=1;
	for (int i=0;i<mod;i+=now){
		s1=mul(s1,s);
		if (mp[s1]) return i+now-mp[s1];
	}
	return 0;
}
void prep(){
	inv[0]=inv[1]=1;
	for (int i=2;i<N;i++) inv[i]=mul(inv[mod%i],(mod-mod/i));
	invf[0]=1;
	for (int i=1;i<N;i++) invf[i]=mul(invf[i-1],inv[i]);
	fac[0]=1;
	for (int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
}
int lalala(int x){
	int now=BSGS(g,x);
	return ksm(g,now/2);
}
void out(poly a){
	for (int i=0;i<a.size();i++) printf("%d ",a[i]);
	puts("\n");
}
inline poly NTT(poly a,int t) {
	for (int i=0;i<a.size();i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
	for (int i=1;i<a.size();i<<=1) {
		int s=(i<<1);
		int wn=ksm(g,(mod-1)/s);
		if (t==-1) wn=ksm(wn,mod-2);
		for (int j=0;j<a.size();j+=s) {
			int w=1;
			for (int k=j;k<j+i;k++) {
				int x=a[k],y=mul(a[k+i],w);
				a[k]=add(x,y); a[k+i]=dec(x,y);
				w=mul(w,wn);
			}
		}
	}
	if (t==-1){
		ll w=ksm(a.size(),mod-2);
		for (int i=0;i<a.size();i++) a[i]=mul(a[i],w);
	}
	return a;
}
inline poly get_down(poly a){
	for (int i=0;i<a.size()-1;i++) a[i]=mul(a[i+1],i+1);
	a.pop_back();
	return a;
}
inline poly get_up(poly a){
	a.push_back(0);
	for (int i=a.size()-1;i>0;i--) a[i]=mul(a[i-1],inv[i]);
	a[0]=0;
	return a; 
}
inline poly operator +(poly a,poly b){
	a.resize(max(a.size(),b.size()));
	b.resize(max(a.size(),b.size()));
	for (int i=0;i<a.size();i++) a[i]=add(a[i],b[i]);
	return a; 
}
inline poly operator -(poly a,poly b){
	a.resize(max(a.size(),b.size()));
	b.resize(max(a.size(),b.size()));
	for (int i=0;i<a.size();i++) a[i]=dec(a[i],b[i]);
	return a;
}
inline poly operator *(poly a,int x){
	x=(mod+x)%mod;
	for (int i=0;i<a.size();i++) a[i]=mul(a[i],x);
	return a;
}
inline poly shift(poly a,long long x,int m){
	long long n=(int)a.size();
	n-=x;
	if (n<=0) return poly(m,0);
	poly b(m,0);
	for (long long i=max(0ll,-x);i<min(n,(ll)m);i++) b[i]=a[i+x];
	return b;
}
inline poly operator *(poly a,poly b){
	int n=a.size(),m=b.size(); 
	int len=init(n+m-1);
	a.resize(len);
	b.resize(len);
	a=NTT(a,1); b=NTT(b,1);
	for (int i=0;i<a.size();i++) a[i]=mul(a[i],b[i]);
	a=NTT(a,-1);
	a.resize(n+m-1);
	return a;
}
inline poly get_inv(poly a){
	poly b,d;
	b.pb(ksm(a[0],mod-2)); d.pb(a[0]);
	int now=1;
	while (now<a.size()) {
		now<<=1; 
		poly c=b;
		c=c*c;
		for (int i=(now>>1);i<now&&i<a.size();i++) d.pb(a[i]);
		c=c*d;c.resize(now); 
		b=b*2;  b=b-c;
	}
	b.resize(a.size());
	return b;
}
inline poly ln(poly a){poly c=get_up(get_inv(a)*get_down(a));c.resize(a.size());return c;}
inline poly exp(poly a){
	poly b,d;
	b.pb(1); d.pb(a[0]);
	int now=1;
	while (now<a.size()){
		now<<=1;
		for (int i=(now>>1);i<now&&i<a.size();i++) d.pb(a[i]);
		poly c=b;
		c.resize(now);
		c=ln(c)-d;
		for (int i=0;i<c.size();i++) c[i]=(mod-c[i])%mod;
		c[0]=add(c[0],1); 
		b=b*c; b.resize(now); 
	}	
	b.resize(a.size());
	return b;
}
inline poly ksm(poly a,int k){
	int n=(int)a.size();
	int i=0;
	while (i<n&&!a[i]) i++;
	if (i==n) return poly(n,0);
	int v=a[i];
	a=a*ksm(v,mod-2);
	a=shift(a,i,n);
	a=exp(ln(a)*k);
	a=shift(a,-1ll*i*k,n);
	a=a*ksm(v,k);
	return a;
}
inline poly Sqrt(poly a){
	poly b,d;
	b.pb(lalala(a[0])); d.pb(a[0]);
	b[0]=min(b[0],mod-b[0]);
	int now=1;
	while (now<a.size()){
		now<<=1; poly c=b;
		for (int i=(now>>1);i<now&&i<a.size();i++) d.pb(a[i]);
		c.resize(now);
		c=get_inv(c);
		c=c*d;c.resize(now);
		b=b+c;
		for (int i=0;i<now;i++) b[i]=mul(b[i],inv[2]);
	}
	b.resize(a.size());
	return b;
}
long long W;
int c[N];
poly f[N];
poly Stirling2_row(int n){
	poly f(n+1,0),g(n+1,0);
	for (int i=0;i<=n;i++) if (i&1) f[i]=dec(0,invf[i]); else f[i]=invf[i];
	for (int i=0;i<=n;i++) g[i]=mul(ksm(i,n),invf[i]);
	f=f*g;
	f.resize(n+1);
	return f;
}
poly solve(int l,int r){
	if (l==r) return f[l];
	else{
		int mid=(l+r)/2;
		return solve(l,mid)*solve(mid+1,r);
	}
}
int main(){
	scanf("%d%lld",&n,&W);
	W%=mod;
	prep();
	for (int i=1;i<=n;i++) scanf("%d",&c[i]);
	for (int i=1;i<=n;i++){
		f[i]=Stirling2_row(c[i]);
		for (int j=0;j<f[i].size();j++) f[i][j]=mul(f[i][j],fac[j]);
	}
	poly ans=solve(1,n);
	int s=1;
	for (int i=1;i<=n;i++) s=1ll*((W+i)%mod)*s%mod*inv[i]%mod;
	int s_fin=0;
	for (int i=0;i<ans.size();i++){
		s_fin=add(s_fin,mul(s,ans[i]));
		s=mul(s,inv[n+i+1]);
		s=1ll*s*((W-i+mod)%mod)%mod;
	}
	printf("%d\n",s_fin);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值