【loj2504】小H爱染色(组合,多项式)

这篇博客介绍了如何解决大规模染色球问题的高效算法,通过组合数学和多项式快速傅里叶变换(FFT)进行计算。文章详细阐述了利用容斥原理和二项式系数的组合意义,将复杂问题转化为多项式卷积,最终通过FFT加速求解。
摘要由CSDN通过智能技术生成

H i H_i Hi 表示给 i i i 个球染色,染两次,每次染 m m m 个,其中要求第一个球必须染。那么题目即求:
∑ i = 0 n − 1 H ( n − i ) F ( i ) \sum_{i=0}^{n-1}H(n-i)F(i) i=0n1H(ni)F(i)
n n n 很大不好求,有种比较神仙的做法。

F ( i ) = ∑ j = 0 m f j i j F(i)=\sum_{j=0}^mf_ji^j F(i)=j=0mfjij,对每个 j j j 分别求解:
∑ j = 0 m f j ∑ i = 0 n − 1 H ( n − i ) i j \sum_{j=0}^mf_j\sum_{i=0}^{n-1}H(n-i)i^j j=0mfji=0n1H(ni)ij
考察 i j i^j ij 的组合意义:对 i i i 个不同的球染 j j j 次色每次染一个的方案数。

刚好我们需要对后面的 n − i n-i ni 个球染两次色每次染 m m m 个。

那么可以考虑直接枚举前面部分染 j j j 次共染的球数 t t t,后面部分共染的球数 k k k,这样就不需要枚举 i i i 了,因为 i i i 就是后面 k k k 个球的第一个。

后面部分的方案数很容易计算,为 ( k m ) ( m 2 m − k ) \binom{k}{m}\binom{m}{2m-k} (mk)(2mkm)

前面部分相当于要求对 t t t 个球染 j j j 次色,其中所有球都被染到的方案数。容斥可以得到 ∑ i = 0 t ( t i ) ( − 1 ) i ( t − i ) j \sum_{i=0}^t\binom{t}{i}(-1)^i(t-i)^j i=0t(it)(1)i(ti)j

于是答案为:
∑ j = 0 m f j ∑ t = 0 m ∑ k = m 2 m ( n t + k ) ( k m ) ( m 2 m − k ) ∑ i = 0 t ( t i ) ( − 1 ) i ( t − i ) j = ∑ t = 0 m ∑ k = m 2 m ( n t + k ) ( k m ) ( m 2 m − k ) ∑ i = 0 t ( t i ) ( − 1 ) i F ( t − i ) \begin{aligned} &\sum_{j=0}^mf_j\sum_{t=0}^m\sum_{k=m}^{2m}\binom{n}{t+k}\binom{k}{m}\binom{m}{2m-k}\sum_{i=0}^t\binom{t}{i}(-1)^i(t-i)^j\\ =&\sum_{t=0}^m\sum_{k=m}^{2m}\binom{n}{t+k}\binom{k}{m}\binom{m}{2m-k}\sum_{i=0}^t\binom{t}{i}(-1)^iF(t-i) \end{aligned} =j=0mfjt=0mk=m2m(t+kn)(mk)(2mkm)i=0t(it)(1)i(ti)jt=0mk=m2m(t+kn)(mk)(2mkm)i=0t(it)(1)iF(ti)
其中 ∑ i = 0 t ( t i ) ( − 1 ) i F ( t − i ) \sum\limits_{i=0}^t\binom{t}{i}(-1)^iF(t-i) i=0t(it)(1)iF(ti) 可以先卷积求出,接着再和只跟 k k k 有关的 ( k m ) ( m 2 m − k ) \binom{k}{m}\binom{m}{2m-k} (mk)(2mkm) 卷起来就能得到 t + k = i t+k=i t+k=i 时的答案,再乘上 ( n t + k ) \binom{n}{t+k} (t+kn) 相加即可。

#include<bits/stdc++.h>

#define LN 22
#define N 1000010

using namespace std;

namespace modular
{
	const int mod=998244353;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;

inline int poww(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,m,fac[N*3],ifac[N*3];
int f[N<<2],g[N<<2];
int rev[N<<2];
vector<int>w[LN][2];

int C(int n,int m)
{
	if(n<0||m<0||m>n) return 0;
	return mul(fac[n],mul(ifac[m],ifac[n-m]));
}

void init(int limit)
{
	for(int bit=0,mid=1;mid<limit;bit++,mid<<=1)
	{
		int len=mid<<1;
		int gn=poww(3,(mod-1)/len);
		int ign=poww(gn,mod-2);
		int g=1,ig=1;
		for(int j=0;j<mid;j++,g=mul(g,gn),ig=mul(ig,ign))
			w[bit][0].push_back(g),w[bit][1].push_back(ig);
	}
}

void NTT(int *a,int limit,int opt)
{
	opt=(opt<0);
	for(int i=0;i<limit;i++)
		rev[i]=(rev[i>>1]>>1)|((i&1)*(limit>>1));
	for(int i=0;i<limit;i++)
		if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int bit=0,mid=1;mid<limit;bit++,mid<<=1)
	{
		for(int i=0,len=mid<<1;i<limit;i+=len)
		{
			for(int j=0;j<mid;j++)
			{
				int x=a[i+j],y=mul(w[bit][opt][j],a[i+mid+j]);
				a[i+j]=add(x,y),a[i+mid+j]=dec(x,y);
			}
		}
	}
	if(opt)
	{
		int tmp=poww(limit,mod-2);
		for(int i=0;i<limit;i++)
			a[i]=mul(a[i],tmp);
	}
}

int main()
{
	n=read(),m=read();
	fac[0]=1;
	for(int i=1;i<=m*3;i++) fac[i]=mul(fac[i-1],i);
	ifac[m*3]=poww(fac[m*3],mod-2);
	for(int i=m*3;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
	int limit=1;
	while(limit<=(m<<1)) limit<<=1;
	init(limit);
	for(int i=0;i<=m;i++) f[i]=mul(ifac[i],read());
	for(int i=0;i<=m;i++) g[i]=mul(ifac[i],(i&1)?mod-1:1);
	NTT(f,limit,1),NTT(g,limit,1);
	for(int i=0;i<limit;i++) f[i]=mul(f[i],g[i]);
	NTT(f,limit,-1);
	for(int i=0;i<=m;i++) f[i]=mul(f[i],fac[i]);
	for(int i=m+1;i<limit;i++) f[i]=0;
	for(int i=0;i<limit;i++) g[i]=0;
	for(int i=m;i<=(m<<1);i++) g[i-m]=mul(C(i,m),C(m,(m<<1)-i));
	NTT(f,limit,1),NTT(g,limit,1);
	for(int i=0;i<limit;i++) f[i]=mul(f[i],g[i]);
	NTT(f,limit,-1);
	int binom=1;
	for(int i=0;i<m;i++) binom=mul(binom,dec(n,i));
	binom=mul(binom,ifac[m]);
	int ans=0;
	for(int i=m;i<=m*3;i++)
	{
		ans=add(ans,mul(binom,f[i-m]));
		binom=mul(binom,dec(n,i));
		binom=mul(binom,mul(ifac[i+1],fac[i]));
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值