题目链接:HDU 6578

有 $n$ 个格子排成一行,从左往右标号为 $1$ 到 $n$。

Tom 想要将每个格子填上 $\{0, 1, 2, 3\}$ 中的一个数字。但是他有 $m$ 条限制:第 $i$ 条限制为区间 $[l_i, r_i]$ 中必须有恰好 $x_i$ 种不同的数字。

请你求出满足所有限制的填数字的方案数量,答案对 $998244353$ 取模。

本题有 $T$ 组数据。

数据范围:$1 \le T \le 15$,$1 \le n \le 100$,$0 \le m \le 100$,$1 \le l_i \le r_i \le n$,$1 \le x_i \le 4$。


Solution

昨天刚在 Codeforces 上做了一道染色题今天又来一道……

发现数字本身是没有意义的,这样状态设计起来就比较方便。我们考虑到当前位置,数字 $0, 1, 2, 3$ 上一次出现的位置分别为 $p_0, p_1, p_2, p_3$,由于数字本身可以认为是「无序」的,于是我们将 $p_0, p_1, p_2, p_3$ 从大到小排序后得到 $i, j, k, l$,即状态 $f(i, j, k, l)$ 表示考虑到当前位置 $i$ 的方案数。

转移方程只需要考虑第 $i +1$ 个位置的数字和 $i, j, k, l$ 中哪一者相同,在此略去。

接下来考虑如何处理限制条件。我们将 $m$ 条限制按照右端点分类,对于当前位置 $i$ 的状态 $f(i, j, k, l)$,我们枚举所有右端点为 $i$ 的限制,如果不满足则将 $f(i, j, k, l)$ 的值改为 $0$。

时间复杂度:$\mathcal O(T(m + n ^ 4))$,带有 $\frac{1}{24}$ 的常数。


Code

#include <cstdio>
#include <cstring>
#include <vector>

const int N = 1e2 + 5;
const int MOD = 998244353;

int T, n, m, f[N][N][N];
std::vector<std::pair<int, int> > vec[N];

void add(int &x, int y) {
    (x += y) >= MOD && (x -= MOD);
}
int main() {
    scanf("%d", &T);
    for (int cs = 1; cs <= T; cs++)  {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) vec[i].clear();
        for (int i = 1, l, r, x; i <= m; i++) {
            scanf("%d%d%d", &l, &r, &x);
            vec[r].emplace_back(std::make_pair(l, x));
        }
        memset(f, 0, sizeof(f));
        f[0][0][0] = 1;
        for (int t = 0; t < n; t++) {
            static int g[N][N][N];
            for (int i = 0; i <= t; i++) {
                for (int j = 0; j <= i; j++) {
                    for (int k = 0; k <= j; k++) {
                        g[i][j][k] = 0;
                    }
                }
            }
            for (int i = 0; i <= t; i++) {
                for (int j = 0; j <= i; j++) {
                    for (int k = 0; k <= j; k++) {
                        if (f[i][j][k] == 0) continue;
                        add(g[i][j][k], f[i][j][k]);
                        add(g[t][i][j], f[i][j][k]);
                        add(g[t][i][k], f[i][j][k]);
                        add(g[t][j][k], f[i][j][k]);
                    }
                }
            }
            for (int i = 0; i <= t; i++) {
                for (int j = 0; j <= i; j++) {
                    for (int k = 0; k <= j; k++) {
                        for (std::pair<int, int> x : vec[t + 1]) {
                            if (1 + (i >= x.first) + (j >= x.first) + (k >= x.first) != x.second) {
                                g[i][j][k] = 0;
                                break;
                            }
                        }
                        f[i][j][k] = g[i][j][k];
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= i; j++) {
                for (int k = 0; k <= j; k++) {
                    add(ans, f[i][j][k]);
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
最后修改:2019 年 07 月 23 日
如果觉得我的文章对你有用,请随意赞赏