0202年写这个是不是晚了啊...算了还是加一句鸡你太美吧...
https://www.luogu.com.cn/problem/P5339
这里是二项式反演的套路做法...
令(F[k])表示恰好有(k)组鸡你太美的方案数
(G[k])表示我钦定(k)组鸡你太美,然后其他位置随便放的方案数
那么答案就是(F[0])
考虑钦定(k)组鸡你太美的位置的方案数,我们把一组鸡你太美缩成(1)个点,那么就有(n-3k)个点,在这些点中随便选(k)个点展开来作为一组鸡你太美就唯一对应了一个安排(k)组鸡你太美的方案,所以钦定位置的方案数是(inom{n-3k}{k})。
然后剩余的人挑(n-4k)个人出来做全排列(下面令(c[i])表示第(i)类人的人数),这就是就是一个生成函数的套路东西,考虑第(i)类人选了(cc[i])((cc[i]le c[i]-k))个,那么这个选剩下的人出来排列的方案数就是
把((n-4k)!)放到外面,然后构造(4)类人的生成函数
(A(x)=sumlimits_{i=0}^{c[1]-k}frac{1}{i!}x^i),(B,C,D)类似(A),分别表示第(2,3,4)类人的生成函数
所以排列的方案数就是((n-4k)![x^{n-4k}]A(x)B(x)C(x)D(x)),注意到良心模数(998244353),(NTT)就好了。
所以(G[k]=inom{n-3k}{k}(n-4k)![x^{n-4k}]A(x)B(x)C(x)D(x))
但我们要求的是(F)...
考虑(F)和(G[k])的关系,枚举最后有(i)组鸡你太美,然后由于是先钦定(k)组,所以(F[i])还要乘上(inom{i}{k}),有
二项式反演得到
答案就是
我们可以在(O(nlog n))的时间内算出(G[i]),最后复杂度(O(n^2 log n))
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;++i)
#define per(i,a,n) for (int i=n-1;i>=a;--i)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
typedef double db;
typedef long long ll;
typedef pair<int,int> PII;
typedef vector<int> VI;
const int N=1e5+10,P=998244353,gn=3,ign=(P+1)/gn;
inline int add(int x,int y) {return (x+=y)>=P?x-P:x;}
inline int sub(int x,int y) {return (x-=y)<0?x+P:x;}
inline int fpow(int x,int y) {
int ret=1; for(;y;y>>=1,x=1ll*x*x%P)
if(y&1) ret=1ll*ret*x%P;
return ret;
}
namespace Poly {
int rev[N];
inline void init(int n) {rep(i,0,n) rev[i]=rev[i>>1]>>1|((i&1)?n>>1:0);}
void ntt(int *f,int n,int flg) {
rep(i,0,n) if(rev[i]<i) swap(f[i],f[rev[i]]);
for(int len=2,k=1;len<=n;len<<=1,k<<=1) {
int wn=fpow(flg==1?gn:ign,(P-1)/len);
for(int i=0;i<n;i+=len)
for(int j=i,w=1;j<i+k;j++,w=1ll*w*wn%P) {
int tmp=1ll*f[j+k]*w%P;
f[j+k]=sub(f[j],tmp),f[j]=add(f[j],tmp);
}
}
if(flg==-1) {
int inv=fpow(n,P-2);
rep(i,0,n) f[i]=1ll*f[i]*inv%P;
}
}
}
using Poly::ntt;
int inv[N],fac[N],ifac[N];
void init(int n) {
fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1;
rep(i,2,n+1) {
inv[i]=1ll*inv[P%i]*(P-P/i)%P;
fac[i]=1ll*fac[i-1]*i%P;
ifac[i]=1ll*ifac[i-1]*inv[i]%P;
}
}
inline int getC(int n,int r) {return n<r?0:1ll*fac[n]*ifac[n-r]%P*ifac[r]%P;}
int F[4][N];
inline int getP(int n,int c1,int c2,int c3,int c4) {
rep(i,0,c1+1) F[0][i]=ifac[i];
rep(i,0,c2+1) F[1][i]=ifac[i];
rep(i,0,c3+1) F[2][i]=ifac[i];
rep(i,0,c4+1) F[3][i]=ifac[i];
int limit=1; while(limit<=c1+c2+c3+c4) limit<<=1; Poly::init(limit);
ntt(F[0],limit,1),ntt(F[1],limit,1),ntt(F[2],limit,1),ntt(F[3],limit,1);
rep(i,0,limit) F[0][i]=1ll*F[0][i]*F[1][i]%P*F[2][i]%P*F[3][i]%P;
ntt(F[0],limit,-1);
int ans=1ll*fac[n]*F[0][n]%P;
rep(i,0,limit) F[0][i]=F[1][i]=F[2][i]=F[3][i]=0;
return ans;
}
int n,c[4];
inline int getG(int k) {return 1ll*getC(n-3*k,k)*getP(n-4*k,c[0]-k,c[1]-k,c[2]-k,c[3]-k)%P;}
int main() {
#ifdef LOCAL
freopen("a.in","r",stdin);
#endif
scanf("%d%d%d%d%d",&n,c,c+1,c+2,c+3);
init(max(n,c[0]+c[1]+c[2]+c[3])); int ans=0,lim=min(n/4,min(c[0],min(c[1],min(c[2],c[3]))));
rep(i,0,lim+1) ans=add(ans,1ll*((i&1)?P-1:1)*getG(i)%P);
printf("%d
",ans);
return 0;
}