[TJOI2019]唱、跳、rap和籃球_生成函數_容斥原理_ntt


[TJOI2019]唱、跳、rap和籃球

這么多人過沒人寫題解啊

那我就隨便說說了嗷

這題第一步挺套路的,就是題目要求不能存在balabala的時候考慮正難則反,要求必須存在的方案數然后用總數減,往往更簡單。

這個題呢直接要求存在發現還不咋好求,反正就是存在嘛我們就容斥好了。

吶,我們就枚舉至少有多少段(唱跳rap籃球)。

假設有\(i\)段,那么枚舉一下這\(i\)段的位置,這是\(\binom{n-3i}{i}\)的。

就相當於給定長度為\(n-3i\)的空格,選出\(i\)個空格為(唱跳rap籃球)的起始點就好了。

假設每種人分別有\(num[1]\)\(num[4]\)個,把他們都\(-i\),就相當於求剩下這些人隨意排列的方案數咯。

假設第\(i\)種人用了\(now[i]\)個,那么方案數為
\(\frac{(n-4i)!}{\prod now[j]!}\)

這東西就是拿生成函數搞一搞就好了。

就是第一個人的生成函數是\(\sum\limits_{i=0}^{num[1]} \frac{x^i}{i!}\)

把這四個生成函數乘一起,最后返回第\(n-4i\)項乘以\((n-4i)!\)就好啦。

貼個代碼

#include <bits/stdc++.h>
#define N 1010 
using namespace std;
typedef long long ll;
const int mod = 998244353 ;
int C[N][N],num[5],len[5],fac[N<<2],inv[N<<2],b[5][N<<2];
int n;
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {int x=0,f=1; char c=nc(); while(c<48) {if(c=='-') f=-1; c=nc();} while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x*f;}
int qpow(int x,int y)
{
	int ans=1;
	while(y)
	{
		if(y&1) ans=(ll)ans*x%mod;
		y>>=1;
		x=(ll)x*x%mod;
	}
	return ans;
}
void init()
{
	fac[0]=1; for(int i=1;i<=1000;i++) fac[i]=(ll)fac[i-1]*i%mod;
	inv[0]=1; for(int i=1;i<=1000;i++) inv[i]=qpow(fac[i],mod-2);
}
void ntt(int *a,int len,int flg)
{
	int i,j,k,t,w,x,tmp;
	for(i=k=0;i<len;i++)
	{
		if(i>k) swap(a[i],a[k]);
		for(j=len>>1;(k^=j)<j;j>>=1);
	}
	for(k=2;k<=len;k<<=1)
	{
		t=k>>1;
		x=qpow(3,(mod-1)/k);
		if(flg==-1) x=qpow(x,mod-2);
		for(i=0;i<len;i+=k)
			for(j=i,w=1;j<i+t;j++)
			{
				tmp=(ll)a[j+t]*w%mod;
				a[j+t]=(a[j]-tmp+mod)%mod;
				a[j]=(a[j]+tmp)%mod;
				w=(ll)w*x%mod;
			}
	}
	if(flg==-1) for(t=qpow(len,mod-2),i=0;i<len;i++) a[i]=(ll)a[i]*t%mod;
}
int calc(int k)
{
	int l=1;
	while(l<=max((num[4]-k)<<1,(n-4*k)<<1)) l<<=1;
	while(l<=(num[4]<<2)) l<<=1;
	for(int i=1;i<=4;i++)
	{
		len[i]=num[i]-k;
		for(int j=0;j<=l;j++) b[i][j]=0;
	}
	for(int i=1;i<=4;i++) for(int j=0;j<=len[i];j++) b[i][j]=inv[j];
	for(int i=1;i<=4;i++) ntt(b[i],l,1);
	for(int i=2;i<=4;i++) for(int j=0;j<l;j++) b[1][j]=(ll)b[1][j]*b[i][j]%mod;
	ntt(b[1],l,-1);
	return (ll)b[1][n-4*k]*fac[n-4*k]%mod;
}
int main()
{
	n=rd(); for(int i=1;i<=4;i++) num[i]=rd();
	for(int i=0;i<=n;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	sort(num+1,num+5);
	int l=1;
	while(l<=(num[4]<<2)) l<<=1;
	init();
	int ans=0;
	for(int i=0;(i<<2)<=min(n,num[1]<<2);i++)
	{
		int mdl=(ll)calc(i)*C[n-3*i][i]%mod;
		if(i&1) (ans-=mdl)%=mod;
		else (ans+=mdl)%=mod;
	}
	printf("%d\n",(ans+mod)%mod);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM