AtCoder Beginner Contest 054

A
题意:

置换群

\[p = \begin{pmatrix} 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 \\ 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 1 \\ \end{pmatrix} \]

\(Alice\)\(Bob\) 给出 \(a_A, a_B\) ,询问谁的 \(p_{a_A}, p_{a_B}\) 的大。

相同则输出 Draw 。

    std::vector<int> p(14), invp(14);
    for (int i = 1; i <= 13; i++) p[i] = i;
    for (int i = 1; i <= 13; i++) std::swap(p[1], p[i]);
    for (int i = 1; i <= 13; i++) invp[p[i]] = i;
    int A, B; std::cin >> A >> B;
    std::cout << (A == B ? "Draw" : p[A] < p[B] ? "Bob" : "Alice") << "\n";

B
题意:

给一个 \(N \times N\) 的网格图和一个 \(M \times M\) 的网格图 \(B\) 。询问 \(B\) 是否是 \(A\) 的子图。

\(1 \leq A, B \leq 50\)

题解:

暴力是在 \(A\)\(O(N^{2})\) 枚举一个左上角,然后再 \(O(N^{2})\) 改子图是否和 \(B\) 相等。

    int N, M; std::cin >> N >> M;
    std::vector<std::vector<char> > A(N + 1, std::vector<char>(N + 1));
    std::vector<std::vector<char> > B(M + 1, std::vector<char>(M + 1));
    for (int i = 1; i <= N; i++) {
        std::string s; std::cin >> s;
        for (int j = 1; j <= N; j++) {
            A[i][j] = s[j - 1];
        }
    }
    for (int i = 1; i <= M; i++) {
        std::string s; std::cin >> s;
        for (int j = 1; j <= M; j++) {
            B[i][j] = s[j - 1];
        }
    }
    int ans = 0;
    for (int i = 1; i + M - 1 <= N; i++) {
        for (int j = 1; j + M - 1 <= N; j++) {
            int ok = 1;
            for (int k = i; k <= i + M - 1; k++) {
                for (int l = j; l <= j + M - 1; l++) {
                    ok &= A[k][l] == B[k - i + 1][l - j + 1];
                }
            }
            ans |= ok;
        }
    }
    std::cout << (ans ? "Yes" : "No") << "\n";

依旧考虑枚举 check ,枚举左上角的复杂度是严格 \(O(N M)\) 。按行哈希后, check 可以优化成 \(O(min(N, M))\) 。总复杂度会是 \(O(N M min(N, M))\)

能不能更快?可以二维哈希。时间复杂度仅有预处理和枚举左上角的 \(O(N M)\)

二维哈希故名思意是二维复合到一维上的 hash 。但是最好只写单哈希,最多调一调 “基” 和 “模数”,否则几乎竞赛现场实现不了。

设第一维的基为 \(B\) ,第二维的基为 \(D\) 。为了方便,这里 \(\bmod 2^{64}\) (比赛现场最好不要)。

将每一行 hash 。

\[h_{i, j} = ha_{i - 1, j} \times B + c_{i, j} \]

在第一维上复合一次哈希(所以为什么用单哈希。多哈希复合多哈希?想想就痛苦)

\[g_{i, j} = g_{i - 1, j} \times D + h_{i, j} \]

单独一维容斥是:

\[\begin{cases} g_{i, [l, r]} &= g_{i, r} - g_{i, l - 1} \times B^{r - l + 1} \\ g_{[u, d], j} &= g_{d, j} - g_{u - 1, j} \times D^{u - d + 1} \\ \end{cases} \]

二维容斥?注意第二维是后复合上去,所以先拿出来:

\[\begin{aligned} g_{[u, d], [l, r]} &= (g_{d, [l, r]}) - (g_{u - 1, [l, r]}) \times D^{d - u + 1} \\ &= \left( g_{d, r} - g_{d, l - 1} \times B^{r - l + 1} \right) - \left( g_{u - 1, r} - g_{u - 1, l - 1} \times B^{r - l + 1} \right) \times D^{u - d + 1} \end{aligned} \]

const int N = 2E5+1;
const int B = 257, D = 527;
int pwb[N], pwd[N];
void solve() {
    pwb[0] = pwd[0] = 1;
    for (int i = 1; i < N; i++) pwb[i] = pwb[i - 1] * B, pwd[i] = pwd[i - 1] * D;
    int n, m; std::cin >> n >> m;
    std::vector<std::vector<u64> > ha(n + 1, std::vector<u64>(n + 1));
    for (int i = 1; i <= n; i++) {
        std::string s; std::cin >> s;
        for (int j = 1; j <= n; j++) {
            ha[i][j] = ha[i][j - 1] * B + s[j - 1];
        }
    }
    std::vector<std::vector<u64> > ga(n + 1, std::vector<u64>(n + 1));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            ga[i][j] = ga[i - 1][j] * D + ha[i][j]; 
        }
    }
    std::vector<std::vector<u64> > hb(m + 1, std::vector<u64>(m + 1));
    for (int i = 1; i <= m; i++) {
        std::string s; std::cin >> s;
        for (int j = 1; j <= m; j++) {
            hb[i][j] = hb[i][j - 1] * B + s[j - 1];
        }
    }
    std::vector<std::vector<u64> > gb(n + 1, std::vector<u64>(n + 1));
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= m; j++) {
            gb[i][j] = gb[i - 1][j] * D + hb[i][j]; 
        }
    }
    auto geta = [&] (int u, int d, int l, int r) -> u64 {
        return (ga[d][r] - ga[d][l - 1] * pwb[r - l + 1])
         - (ga[u - 1][r] - ga[u - 1][l - 1] * pwb[r - l + 1]) * pwd[d - u + 1];
    };
    auto getb = [&] (int u, int d, int l, int r) -> u64 {
        return (gb[d][r] - gb[d][l - 1] * pwb[r - l + 1])
         - (gb[u - 1][r] - gb[u - 1][l - 1] * pwb[r - l + 1]) * pwd[d - u + 1];
    };
    int ok = 0;
    for (int i = 1; i + m - 1 <= n; i++) {
        for (int j = 1; j + m - 1 <= n; j++) {
            if (getb(1, m, 1, m) == geta(i, i + m - 1, j, j + m - 1)) {
                ok |= 1;
            }
        }
    }
    std::cout << (ok ? "Yes" : "No") << "\n";
}

板子题是

Matrix Matcher
https://www.luogu.com.cn/problem/UVA11019

const int N = 2E5+1;
const int B = 257, D = 527;
int pwb[N], pwd[N];
void solve() {
    pwb[0] = pwd[0] = 1;
    for (int i = 1; i < N; i++) pwb[i] = pwb[i - 1] * B, pwd[i] = pwd[i - 1] * D;
    int n, m; std::cin >> n >> m;
    std::vector<std::vector<u64> > ha(n + 1, std::vector<u64>(m + 1));
    for (int i = 1; i <= n; i++) {
        std::string s; std::cin >> s;
        for (int j = 1; j <= m; j++) {
            ha[i][j] = ha[i][j - 1] * B + s[j - 1];
        }
    }
    std::vector<std::vector<u64> > ga(n + 1, std::vector<u64>(m + 1));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            ga[i][j] = ga[i - 1][j] * D + ha[i][j]; 
        }
    }
    int h, w; std::cin >> h >> w;
    std::vector<std::vector<u64> > hb(h + 1, std::vector<u64>(w + 1));
    for (int i = 1; i <= h; i++) {
        std::string s; std::cin >> s;
        for (int j = 1; j <= w; j++) {
            hb[i][j] = hb[i][j - 1] * B + s[j - 1];
        }
    }
    std::vector<std::vector<u64> > gb(h + 1, std::vector<u64>(w + 1));
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            gb[i][j] = gb[i - 1][j] * D + hb[i][j]; 
        }
    }
    auto geta = [&] (int u, int d, int l, int r) -> u64 {
        return (ga[d][r] - ga[d][l - 1] * pwb[r - l + 1])
         - (ga[u - 1][r] - ga[u - 1][l - 1] * pwb[r - l + 1]) * pwd[d - u + 1];
    };
    auto getb = [&] (int u, int d, int l, int r) -> u64 {
        return (gb[d][r] - gb[d][l - 1] * pwb[r - l + 1])
         - (gb[u - 1][r] - gb[u - 1][l - 1] * pwb[r - l + 1]) * pwd[d - u + 1];
    };
    int ok = 0;
    for (int i = 1; i + h - 1 <= n; i++) {
        for (int j = 1; j + w - 1 <= n; j++) {
            if (getb(1, h, 1, w) == geta(i, i + h - 1, j, j + w - 1)) {
                ok += 1;
            }
        }
    }
    // std::cout << (ok ? "Yes" : "No") << "\n";
    std::cout << ok << "\n";
}

C
题意:

给一个没有自环和重边的 \(N\) 个点 \(M\) 条边的无向无权图。
\(i(1 \leq i \leq M)\) 条边连接点 \(a_i, b_i\)
询问有多少条路径从 \(1\) 出发,且最终恰好访问 \(N\) 个节点。

\(2 \leq n \leq 8, 0 \leq M \leq \frac{N(N - 1)}{2}, 1 \leq a_i, b_i \leq N\)

题意:

对访问过的点标记,dfs 一定能遍历所有路径,统计满足条件的路径。

时间复杂度是 \(O(N!)\)

能不能更快?应该不是很能。

D
题意:

海豚准备制造一些化学物质 \(C\)\(C\) 由物质 \(A\) 和物质 \(B\)\(M_a : M_b\) 的比率混合而成。
药店中销售 \(N\) 种药物,每种药物只有一包库存。第 \(i\) 种药物包含 \(a_i\)\(A\)\(b_i\)\(B\) ,售价为 \(c_i\)
海豚可以在药典买一些药物,但所有买了的药物的物质 \(A\) 和物质 \(B\) 必须全部用于制造物质 \(C\)
询问生成物质 \(C\) 最少需要支付的代价。或表述这是不可能的。

\(1 \leq N \leq 40, 1 \leq a_i, b_i \leq 10, 1 \leq c_i \leq 100, 1 \leq M_a, M_ \leq 10, gcd(M_a, M_b) = 1\)

题解:

最简单的切入点是,可以先尝试处理出最终贡献:考虑了所有药物(不一定买),获得 \(i\)\(A\)\(j\)\(B\) 最少需要的价钱。假设是 \(f_{i, j}\)

然后在最终贡献里找答案,查询 \(f_{k \times M_a, k \times M_b}\)

怎么得到 \(f\) ?经典模型:从一堆元素中选出某些元素,可能会附加一些状态。

那么大概率由小集合往大集合 DP 就行,然后分析是否真的能 DP 。

考虑 \(f_{i, j, k}\) 为:考虑了前 \(i\) 种药物,能得到 \(j\)\(A\)\(k\)\(B\) 需要的最少消费。(不能到达的状态需要正无穷消费)

怎么 \(DP\) ?其实这时候已经是个非常典型的 DP 公式了。

\[f_{i + 1, j, k} \leftarrow \begin{cases} f_{i, j, k} \\ f_{i, j - a_{i + 1}, k - b_{i + 1}} \\ \end{cases} \]

答案就是枚举 \(c\) ,检查 \(f_{n, c \times M_a, c \times M_b}\)

DP 只是工具,重要的是 DP 之前,能明白问题切入点。

posted @ 2023-03-18 18:49  03Goose  阅读(27)  评论(0)    收藏  举报