题目
题解
题意
有一个
n
n
n层的树
每层有
a
i
a_i
ai个位置可以放装饰品
装饰品共有
m
m
m种颜色
要求同一层相邻两个装饰品颜色不能相同,相邻两层的颜色集合不能相同
求出合法的方案数,对读入的
p
p
p取模
分析
D
P
DP
DP
思考同一层内的情况
设
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示到了第
i
i
i个位置,用了
j
j
j种颜色,并且强制要求第1种出现的颜色一定是1,第2种一定是2,第3种一定是3,等等等等
那么有转移方程:
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)
g[i][j]=g[i−1][j−1]+g[i−1][j]∗(j−1)
解释:
若前面
i
−
1
i-1
i−1个位置用了
j
−
1
j-1
j−1种颜色,那么当前这个位置必须选第
j
j
j种颜色
若前面
i
−
1
i-1
i−1个位置用
j
j
j种颜色,那么当前这个位置可以随便选颜色,但是不能跟上一个位置的颜色相同,即有
j
−
1
j-1
j−1种颜色可以选
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示到了第
i
i
i层用了
j
j
j种颜色
有转移方程:
f
[
i
]
[
j
]
=
(
g
[
a
[
i
]
]
[
j
]
∗
C
m
j
∗
j
!
∗
∑
k
=
1
a
[
i
−
1
]
f
[
i
−
1
]
[
k
]
)
−
f
[
i
−
1
]
[
j
]
∗
g
[
a
[
i
]
]
[
j
]
∗
j
!
f[i][j]=(g[a[i]][j]*C_m^j*j!*\sum_{k=1}^{a[i-1]}f[i-1][k])-f[i-1][j]*g[a[i]][j]*j!
f[i][j]=(g[a[i]][j]∗Cmj∗j!∗k=1∑a[i−1]f[i−1][k])−f[i−1][j]∗g[a[i]][j]∗j!
解释
首先对于
j
j
j种颜色
在
a
[
i
]
a[i]
a[i]位置里放
j
j
j种颜色的方案数是
g
[
a
[
i
]
]
[
j
]
g[a[i]][j]
g[a[i]][j]
而这
j
j
j种颜色可以在
m
m
m种颜色里随便选,乘上
C
m
j
C_m^j
Cmj
这
j
j
j种颜色又有
j
!
j!
j!排列方式,乘上
j
!
j!
j!
然后可以从
i
−
1
i-1
i−1层选了
k
k
k种颜色转移过来,乘上
∑
k
=
1
a
[
i
−
1
]
f
[
i
−
1
]
[
k
]
\sum_{k=1}^{a[i-1]}f[i-1][k]
∑k=1a[i−1]f[i−1][k]
然后减去重复的部分
即在第
i
−
1
i-1
i−1层选
j
j
j种颜色,这
j
j
j种颜色在第
i
i
i层有
g
[
a
[
i
]
]
[
j
]
g[a[i]][j]
g[a[i]][j]种方案,
j
j
j种颜色有
j
!
j!
j!种排列方法,所以答案要减去
f
[
i
−
1
]
[
j
]
∗
g
[
a
[
i
]
]
[
j
]
∗
j
!
f[i-1][j]*g[a[i]][j]*j!
f[i−1][j]∗g[a[i]][j]∗j!
看到转移方程中有
C
m
j
C_m^j
Cmj
一般用逆元来求
但是这题的模数不一定是质数,用不了费马小定理
思考:
C
m
j
∗
j
!
=
m
!
j
!
(
m
−
j
)
!
∗
j
!
=
m
!
(
m
−
j
)
!
C_m^j*j!=\dfrac{m!}{j!(m-j)!}*j!=\dfrac{m!}{(m-j)!}
Cmj∗j!=j!(m−j)!m!∗j!=(m−j)!m!
设
P
[
j
]
P[j]
P[j]表示
C
m
j
∗
j
!
C_m^j*j!
Cmj∗j!
那么
P
[
j
]
P[j]
P[j]是可以递推的
P
[
j
]
=
P
[
j
−
1
]
∗
(
m
−
j
+
1
)
P[j]=P[j-1]*(m-j+1)
P[j]=P[j−1]∗(m−j+1)
至于为什么自己推一下
预处理
P
P
P和阶乘即可
时间复杂度
O
(
S
)
O(S)
O(S)
但空间可能炸掉
所以要滚动数组
Code
#include<cstdio>
#include<iostream>
using namespace std;
int n,m,i,j,one,two;
long long mod,ans,mx,g[5005][5005],f[3][5005],s[1000005],jc[1000005],a[1000005];
int main()
{
freopen("kalanchoe.in","r",stdin);
freopen("kalanchoe.out","w",stdout);
scanf("%d%d%lld",&n,&m,&mod);
for (i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
mx=max(mx,a[i]);
}
g[0][0]=1;
for (i=1;i<=5000;i++)
for (j=1;j<=i&&j<=m;j++)
g[i][j]=(g[i-1][j-1]+g[i-1][j]*(j-1)%mod)%mod;
s[0]=1;
jc[0]=1;
for (i=1;i<=m&&i<=5000;i++)
{
s[i]=s[i-1]*(m-i+1)%mod;
jc[i]=jc[i-1]*i%mod;
}
one=1;
two=0;
f[1][0]=1;
ans=1;
for (i=1;i<=n;i++)
{
swap(one,two);
for (j=0;j<=a[i];j++)
f[one][j]=(ans*g[a[i]][j]%mod)*s[j]%mod;
for (j=0;j<=a[i-1];j++)
if (f[two][j]!=0)
{
f[one][j]=(f[one][j]-(f[two][j]*g[a[i]][j]%mod)*jc[j]%mod)%mod;
f[two][j]=0;
}
ans=0;
for (j=0;j<=a[i];j++)
ans=(ans+f[one][j])%mod;
}
printf("%lld\n",(ans+mod)%mod);
fclose(stdin);
fclose(stdout);
return 0;
}