1. 程式人生 > >【BZOJ3294/洛谷3158】[CQOI2011]放棋子(組合數+DP)

【BZOJ3294/洛谷3158】[CQOI2011]放棋子(組合數+DP)

題目:

洛谷3158

分析:

某OIer兔崽子的此題程式碼中的三個函式名:dfs、ddfs、dddfs(充滿毒瘤的氣息

顯然,行與行之間、列與列之間是互相獨立的。考慮揹包,用\(f[k][i][j]\)表示用前\(k\)種顏色佔了\(i\)\(j\)列的方案數,\(g[i][j]\)表示用顏色\(k\)佔據\(i\)\(j\)列的方案數,\(c[i]\)表示顏色為\(i\)的棋子數,就有如下方程:

\[f[k][i][j]=\sum _{a=0}^i \sum_{b=0}^j f[k-1][i-a][j-b]\times g[a][b]\times C_{n-(i-a)}^a\times C_{m-(j-b)}^b(ab\geq c[i])\]

\(g[i][j]\)在算的時候注意要減去有空行或空列的情況(列舉有多少行、列不是空的)。注意要\(a=i\)\(b=j\)的情況要跳過:

\[g[i][j]=C_{ij}^{c[k]}-\sum_{a=0}^i \sum_{b=0}^j g[a][b]\times C_i^a \times C_j^b(ij\geq c[k])\]

程式碼:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;

namespace zyt
{
    template<typename T>
    inline void read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != '-' && !isdigit(c));
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar('-'), x = -x;
        do
            *pos++ = x % 10 + '0';
        while (x /= 10);
        while (pos > buf)   
            putchar(*--pos);
    }
    typedef long long ll;
    const int N = 40, T = 20, p = 1e9 + 9;
    int n, m, c, arr[T], C[N * N][N * N], f[T][N][N], g[N][N];
    void init()
    {
        for (int i = 0; i < N * N; i++)
        {
            C[i][0] = 1;
            for (int j = 1; j <= i; j++)
                C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % p;
        }
    }
    int work()
    {
        init();
        read(n), read(m), read(c);
        for (int i = 1; i <= c; i++)
            read(arr[i]);
        f[0][0][0] = 1;
        for (int k = 1; k <= c; k++)
        {
            memset(g, 0, sizeof(g));
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= m; j++)
                {
                    if (i * j >= arr[k])
                    {
                        g[i][j] = C[i * j][arr[k]];
                        for (int a = 0; a <= i; a++)
                            for (int b = 0; b <= j; b++)
                            {
                                if (a != i || b != j)
                                    g[i][j] = (g[i][j] - (ll)g[a][b] * 
                                        C[i][a] % p * C[j][b] % p + p) % p;
                            }
                    }
                }
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= m; j++)
                    for (int a = 1; a <= i; a++)
                        for (int b = 1; b <= j; b++)
                            if (a * b >= arr[k])
                                f[k][i][j] = (f[k][i][j] + 
                                    (ll)f[k - 1][i - a][j - b] * g[a][b] % p * 
                                    C[n - (i - a)][a] % p * C[m - (j - b)][b]) % p;
        }
        int ans = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                ans = (ans + f[c][i][j]) % p;
        write(ans);
        return 0;
    }
}
int main()
{
    return zyt::work();
}