problem
大中锋的学院要组织学生参观博物馆,要求学生们在博物馆中排成一队进行参观。
他的同学可以分为四类:一部分最喜欢唱、一部分最喜欢跳、一部分最喜欢 rap,还有一部分最喜欢篮球。
若队列中
k
,
k
+
1
,
k
+
2
,
k
+
3
k,k+1,k+2,k+3
k,k+1,k+2,k+3 位置上的同学依次,最喜欢唱、最喜欢跳、最喜欢 rap、最喜欢篮球,他们就会聚在一起讨论蔡徐坤。
大中锋不希望这种事情发生,因为这会使得队伍显得很乱。
大中锋想知道有多少种排队的方法,不会有学生聚在一起讨论蔡徐坤。
两个学生队伍被认为是不同的,当且仅当两个队伍中至少有一个位置上的学生的喜好不同。
由于合法的队伍可能会有很多种,种类数对
998244353
998244353
998244353 取模。
学生中最喜欢唱的人数、最喜欢跳的人数、最喜欢 rap 的人数和最喜欢篮球的人数分别为 A , B , C , D A,B,C,D A,B,C,D。
数据范围: n ≤ 1000 n≤1000 n≤1000, A , B , C , D ≤ 500 A,B,C,D≤500 A,B,C,D≤500。
solution
为了方便,我们把要讨论蔡徐坤的段称为 “ “ “cxk 段 ” ” ”。
那么一个比较显然的思路就是枚举 cxk 段的数量然后容斥。从 n n n 个数中选 i i i 个 cxk 段的方案数是 C n − 3 i i C_{n-3i}^{i} Cn−3ii,因为每个 cxk 段占 4 4 4 个位置,那么段头只能在 n − 3 i n-3i n−3i 个位置里面选,所以:
a n s = ∑ i = 0 min ( a , b , c , d , ⌊ n 4 ⌋ ) ( − 1 ) i ( n − 3 i i ) f i ans=\sum_{i=0}^{\min(a,b,c,d,\lfloor\frac n 4\rfloor)}(-1)^i\binom {n-3i} i f_i ans=i=0∑min(a,b,c,d,⌊4n⌋)(−1)i(in−3i)fi
其中 f i f_i fi 表示在剩下的 n − 4 i n-4i n−4i 个位置上选人的方案数。现在每个部分剩下的人分别有 A − i , B − i , C − i , D − i A-i,B-i,C-i,D-i A−i,B−i,C−i,D−i 个。
这就相当于求有重复元素的排列。
如果 A + B + C + D = n A+B+C+D=n A+B+C+D=n 的话,这一部分贡献应是 ( n − 4 i ) ! ( A − i ) ! ( B − i ) ! ( C − i ) ! ( D − i ) ! \frac{(n-4i)!}{(A-i)!(B-i)!(C-i)!(D-i)!} (A−i)!(B−i)!(C−i)!(D−i)!(n−4i)!。
那么如果 A + B + C + D > n A+B+C+D>n A+B+C+D>n 的话,我们直接枚举,即:
f i = ∑ a = 0 A − i ∑ b = 0 B − i ∑ c = 0 C − i ∑ d = 0 D − i [ a + b + c + d = n − 4 i ] ( n − 4 i ) ! a ! b ! c ! d ! = ( n − 4 i ) ! ∑ a = 0 A − i ∑ b = 0 B − i ∑ c = 0 C − i ∑ d = 0 D − i [ a + b + c + d = n − 4 i ] 1 a ! 1 b ! 1 c ! 1 d ! \begin{aligned} f_i&=\sum_{a=0}^{A-i}\sum_{b=0}^{B-i}\sum_{c=0}^{C-i}\sum_{d=0}^{D-i}[a+b+c+d=n-4i]\frac{(n-4i)!}{a!b!c!d!}\\ &=(n-4i)!\sum_{a=0}^{A-i}\sum_{b=0}^{B-i}\sum_{c=0}^{C-i}\sum_{d=0}^{D-i}[a+b+c+d=n-4i]\frac{1}{a!}\frac{1}{b!}\frac{1}{c!}\frac{1}{d!} \end{aligned} fi=a=0∑A−ib=0∑B−ic=0∑C−id=0∑D−i[a+b+c+d=n−4i]a!b!c!d!(n−4i)!=(n−4i)!a=0∑A−ib=0∑B−ic=0∑C−id=0∑D−i[a+b+c+d=n−4i]a!1b!1c!1d!1
这是一个四维的卷积,直接 n t t ntt ntt 做就可以了。
时间复杂度 O ( n 2 log n ) O(n^2\log n) O(n2logn)。
code
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 5005
#define P 998244353
using namespace std;
typedef vector<int> poly;
const int g=3;
int n,a,b,c,d,up,ans,pos[N],fac[N],ifac[N];
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y%P;}
int power(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
return ans;
}
int L=12,*w[13];
void init_fac(){
fac[0]=fac[1]=1;
for(int i=2;i<N;++i) fac[i]=mul(fac[i-1],i);
ifac[N-1]=power(fac[N-1],P-2);
for(int i=N-2;~i;--i) ifac[i]=mul(ifac[i+1],i+1);
}
int C(int n,int m) {return mul(fac[n],mul(ifac[m],ifac[n-m]));}
void init_w(){
for(int i=1;i<=L;++i)
w[i]=new int[1<<(i-1)];
int now=power(g,(P-1)/(1<<L));
w[L][0]=1;
for(int i=1;i<(1<<(L-1));++i) w[L][i]=mul(w[L][i-1],now);
for(int i=L-1;i;--i)
for(int j=0;j<(1<<(i-1));++j)
w[i][j]=w[i+1][j<<1];
}
void init(int lim){
for(int i=0;i<lim;++i) pos[i]=(pos[i>>1]>>1)|((i&1)*(lim>>1));
}
void NTT(poly &f,int lim,int type){
for(int i=0;i<lim;++i)
if(pos[i]>i) swap(f[i],f[pos[i]]);
for(int mid=1,l=1;mid<lim;mid<<=1,++l){
for(int i=0;i<lim;i+=(mid<<1)){
for(int j=0;j<mid;++j){
int p0=f[i+j],p1=mul(f[i+j+mid],w[l][j]);
f[i+j]=add(p0,p1),f[i+j+mid]=dec(p0,p1);
}
}
}
if(type==-1&&(reverse(f.begin()+1,f.begin()+lim),1)){
int inv=power(lim,P-2);
for(int i=0;i<lim;++i) f[i]=mul(f[i],inv);
}
}
int calc(int x){
int ans=C(n-3*x,x);
poly A(0),B(0),C(0),D(0);
for(int i=0;i<=a-x;++i) A.push_back(ifac[i]);
for(int i=0;i<=b-x;++i) B.push_back(ifac[i]);
for(int i=0;i<=c-x;++i) C.push_back(ifac[i]);
for(int i=0;i<=d-x;++i) D.push_back(ifac[i]);
int lim=1,len=A.size()+B.size()+C.size()+D.size()-3;
while(lim<len)lim<<=1;init(lim);
A.resize(lim),NTT(A,lim,1);
B.resize(lim),NTT(B,lim,1);
C.resize(lim),NTT(C,lim,1);
D.resize(lim),NTT(D,lim,1);
for(int i=0;i<lim;++i) A[i]=1ll*A[i]*B[i]%P*C[i]%P*D[i]%P;
NTT(A,lim,-1),A.resize(len);
return mul(ans,mul(fac[n-4*x],A[n-4*x]));
}
int main(){
init_w(),init_fac();
scanf("%d%d%d%d%d%d",&n,&a,&b,&c,&d);
up=min(min(min(a,b),min(c,d)),n/4);
for(int i=0;i<=up;++i){
ans=(i&1)?dec(ans,calc(i)):add(ans,calc(i));
}
printf("%d\n",ans);
return 0;
}