n
,
m
<
=
1
e
6
,
a
i
<
=
5
e
3
,
∑
a
i
<
=
1
e
6
n,m<=1e6,a_i<=5e3,\sum a_i <= 1e6
n,m<=1e6,ai<=5e3,∑ai<=1e6
不
用
谢
。
\text 不用谢。
不用谢。
首先每一层的答案只与颜色的数量有关。
但是相邻层的颜色集合不能相同。
预处理
g
i
,
j
g_{i,j}
gi,j表示
i
i
i个位置分配给
j
j
j种颜色并且每种都出现并且相邻的颜色不同的方案数,注意我们让
g
g
g中统计的方案颜色无标号之分,也就是
g
i
,
j
=
g
i
−
1
,
j
−
1
+
g
i
−
1
,
j
∗
(
j
−
1
)
g_{i,j} = g_{i-1,j-1} + g_{i-1,j}*(j-1)
gi,j=gi−1,j−1+gi−1,j∗(j−1),这样可以在之后的
D
P
\rm DP
DP中可以免去求逆元的环节。
f
i
,
j
f_{i,j}
fi,j表示前
i
i
i层,第
i
i
i层用了
j
j
j种颜色的方案。
f
i
,
j
=
g
a
i
,
j
(
m
j
)
j
!
∑
k
f
i
−
1
,
k
−
f
i
−
1
,
j
∗
g
i
−
1
,
j
j
!
f_{i,j} = g_{a_i,j}\binom mj j!\sum_{k}f_{i-1,k} - f_{i-1,j}*g_{i-1,j}j!
fi,j=gai,j(jm)j!∑kfi−1,k−fi−1,j∗gi−1,jj!
这里的
(
m
j
)
j
!
\binom mj j!
(jm)j!可以简单预处理递推得到而不用求逆元。
A C C o d e \rm AC\ Code AC Code
#include<bits/stdc++.h>
#define maxn 10000005
using namespace std;
int n,m,p;
int a[maxn],g[5005][5005],f[2][5005],Am[5005],fac[5005];
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%d%d%d",&n,&m,&p);
g[0][0] = 1;
for(int i=1;i<=5000;i++)
for(int j=1;j<=i && j<=m;j++)
g[i][j] = ((j ? g[i-1][j-1] : 0) + 1ll * g[i-1][j] * (j-1)) % p;
Am[0] = fac[0] = 1;
for(int i=1;i<=min(5000,m);i++) Am[i] = 1ll * Am[i-1] * (m-i+1) % p , fac[i] = 1ll * fac[i-1] * i % p;
int now = 1 , pre = 0 , sum = 0;f[now][0] = sum = 1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
swap(now,pre);
for(int j=0;j<=a[i];j++)
f[now][j] = sum * 1ll * g[a[i]][j] % p * Am[j] % p;
for(int j=0;j<=a[i-1];j++) if(f[pre][j]){
f[now][j] = (f[now][j] - 1ll * g[a[i]][j] * f[pre][j] % p * fac[j]) % p;
f[pre][j] = 0;
}
sum = 0;
for(int j=0;j<=a[i];j++) sum = (sum + f[now][j]) % p /* , printf("%d %d %d\n",i,j,f[now][j])*/;
}
printf("%d\n",(sum+p)%p);
}