那啥,其实。。。cxk是我(b站上的)最喜欢的偶像。
话说没有朴素的$O(n^3)$容斥+$DP$的题解诶。。。
一脸套路容斥。枚举有$i$个不合法组,容斥系数$(-1)^i$,$C_{n-3i}^i$选位置,剩下了$a-i$、$b-i$、$c-i$、$d-i$个人和$n-4i$个位置,要给他们任意安排位置。
直接组合数学不太会做的样子,咱也不会$NTT$和生成函数,考虑$DP$。
设$f(i,j)$为前$i$种爱好放了$j$个人的方案数。
枚举每种爱好有多少人,插进当前的队伍里:$f(i,j)=\sum\limits_{k=0}^{a/b/c/d}f(i-1,j-k)\times C_j^{j-k}$
套路容斥:$ans=\sum\limits_{i=0}^{\min\{\frac{n}{4},a,b,c,d\}}(-1)^iC_{n-3i}^if(4,n-4i)$。注意每个$i$的$a,b,c,d$都不一样,需要重新$DP$。
复杂度$O(n^3)$,然而实际上常数极小甚至(应该)到不了$\dfrac{1}{10}$。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 1005
#define inf 0x3f3f3f3f
const int mod = 998244353;
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
int f[maxn],c[maxn][maxn],a[5];
inline int calc(int n){
memset(f,0,sizeof f);
f[0]=1;
for(register int i=1,sum=0;i<=4;++i){
sum=min(sum+a[i],n);
for(register int j=sum;j;--j)
for(register int k=min(j,a[i]);k;--k)
(f[j]+=1ll*f[j-k]*c[j][j-k]%mod)%=mod;
--a[i];
}
return f[n];
}
inline long long pow1(int x){return x&1?-1ll:1ll;}
int main(){
int n=read(),mi=n>>2,ans=0;
for(register int i=1;i<=4;++i)mi=min(a[i]=read(),mi);
c[0][0]=1;
for(register int i=1;i<=n;++i){
c[i][0]=1;
for(register int j=1;j<=i;++j)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
for(register int i=0;i<=mi;++i)(ans+=pow1(i)*c[n-i*3][i]*calc(n-(i<<2))%mod)%=mod;
printf("%d\n",(ans+mod)%mod);
}