jzoj 3424. 【NOIP2013模拟】粉刷匠

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 
#include 
#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;
}

你可能感兴趣的:(jzoj 3424. 【NOIP2013模拟】粉刷匠)