[洛谷] P2476 [SCOI2008]着色方案 & P4448 [AHOI2018初中组]球球的排列
Description
Solution
P2476
注意到每个位置要填一种颜色,要求是不能有相邻的颜色。于是我们设\(s[i]\)为\(a[i]\)前缀和,\(dp[i][j]\)表示用前\(i\)种颜色填了前\(s[i]\)个位置,恰有\(j\)组相邻位置同色的方案数。再枚举\(x,y\),表示将第\(i+1\)种颜色分成\(x\)组,其中有\(y\)组插入之前相邻的同色位置中间,\(x-y\)组插空放,不相邻。则\(dp[i][j]\rightarrow{dp[i+1][j-y+a[i+1]-x]}\)。
转移系数就是把三步方案数乘起来:
- 将第\(i+1\)种颜色分成\(x\)组:\(\dbinom{a[i+1]-1}{x-1}\)
- 其中有\(y\)组插入之前相邻的同色位置中间:\(\dbinom{j}{y}\)
- \(x-y\)组插空放,不相邻:\(\dbinom{s[i]+1-j}{x-y}\)(不能放在之前相邻的同色位置中间)
初始化\(dp[1][a[1]-1]=1\),答案为\(dp[n][0]\),时间复杂度为\(O(n^3)\)
P4448
我们把每个\(a[i]\)都除以它的平方因子后,原问题等价于有多少种排列方式,使得没有相邻位置的\(a[i]\)相同。
设每个不同值的\(a[i]\)有\(s[i]\)个。考虑到P2476是每个位置任意染颜色,而P4448是先给每个位置染上颜色后,再进行任意排列。这两个问题不同之处在与,前者每种颜色内部之间不能区分顺序,而后者可以。所以直接按照前者跑出\(dp[n][0]\)后,再乘上\(\prod{s[i]!}\)即可。
Code(P2476)
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int n, a[20], s[20], dp[20][80], c[80][80];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * fl;
}
int main(){
n = read();
for (int i = 1; i <= n; i ++ ) a[i] = read(), s[i] = s[i - 1] + a[i];
c[0][0] = 1; for (int i = 1; i <= 75; i ++ ) {c[i][0] = 1; for (int j = 1; j <= 75; j ++ ) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;}
dp[1][a[1] - 1] = 1;
for (int i = 1; i <= n - 1; i ++ )
{
for (int j = 0; j <= s[i] - 1; j ++ )
{
if (!dp[i][j]) continue;
for (int x = 1; x <= a[i + 1]; x ++ )
for (int y = 0; y <= min(j, x); y ++ )
dp[i + 1][j - y + a[i + 1] - x] = (dp[i + 1][j - y + a[i + 1] - x] + 1ll * dp[i][j] * c[j][y] % mod * c[a[i + 1] - 1][x - 1] % mod * c[s[i] + 1 - j][x - y] % mod) % mod;
}
}
printf("%d\n", dp[n][0]);
return 0;
}