Description
有\(K\)种小球,每种小球有\(Ci\)个,求相邻小球颜色不相同的方案数。
100% \(K≤15,Ci≤6,T≤2000\)
Solution
一开始以为是一道结论题,没想到竟然是DP。
我们设\(f[i][j]\)表示刷到第i种颜色,当前有j个相邻柱子颜色相同的方案数。
记刷到现在的颜色块数为\(S\)(\(c[1]\)~\(c[i]\))
对于第\(i+1\)种颜色,能刷\(c[i+1]\)次。
而我们把它分成k块,插入已有块中。
而其中有\(l\)块是插在相邻柱子颜色相同的中间。
如此,便可得出转移方程:
\[f[i+1][j+c[i+1-k-l]]+=f[i][j]*C(j,l)*C(c[i+1]-1,k-1)*C(n-j+1,k-l)\]
Code
#include <cstdio>
#include <cstring>
#define K 16
#define N 110
#define mo 1000000007
#define ll long long
#define mem(a, x) memset(a, x, sizeof a)
#define fo(x, a, b) for (int x = a; x <= b; x++)
#define fd(x, a, b) for (int x = a; x >= b; x--)
using namespace std;
int T, n, m, c[N];
ll f[N][N], jc[N], ny[N];
inline int read()
{
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return x;
}
ll ksm(ll x, int y)
{
ll s = 1;
while (y)
{
if (y & 1) s = s * x % mo;
x = x * x % mo; y >>= 1;
}
return s;
}
ll C(ll x, ll y) {return jc[x] * ny[y] % mo * ny[x - y] % mo;}
int main()
{
freopen("paint.in", "r", stdin);
freopen("paint.out", "w", stdout);
jc[0] = 1; fo(i, 1, 90) jc[i] = jc[i - 1] * i % mo;
ny[90] = ksm(jc[90], mo - 2);
fd(i, 89, 0) ny[i] = ny[i + 1] * (i + 1) % mo;
T = read();
while (T--)
{
m = read(); n = 0;
fo(i, 1, m) c[i] = read();
mem(f, 0);
f[1][c[1] - 1] = 1;
fo(i, 1, m - 1)
{
n += c[i];
fo(j, 0, n)
fo(k, 1, c[i + 1])
fo(l, 0, (j < k ? j : k))
f[i + 1][j + c[i + 1] - k - l] += f[i][j] * C(j, l) % mo * C(c[i + 1] - 1, k - 1) % mo * C(n - j + 1, k - l) % mo;
}
printf("%lld\n", f[m][0] % mo);
}
return 0;
}