abc 391 题解

ABC

D

解题思路

如果对于每个点,我们知道了它被移除的时刻(无限大则说明不会被移除),我们就可以知道询问的点在给定时刻有没有消失。

在对所有的点按横坐标分类后,我们就可以知道最多有多少行点被移除:点数最少的那一列的点数。

对于能够被移除的点,假设它处在 \(x\) 层,则被该点被移除的时刻就是第 \(x\) 层被移除的时刻 \(t[x] = max(t[x - 1] + 1, max_{点 i 在 x 层}Y_i)\)

CODE
void solve()
{
    int n = 0, w = 0;
    std::cin >> n >> w;
    // 按横坐标分类
    std::vector X(w + 1, std::vector<int>{});
    // 每个点在第几层
    std::vector idx(n, 0);
    for (int i = 0; i < n; i++) {
        int x = 0, y = 0;
        std::cin >> x >> y;
        idx[i] = X[x].size();
        X[x].push_back(y);
    }
    int mn = n;
    for (int i = 1; i <= w; i++) {
        mn = std::min(mn, int(X[i].size()));
    }

    std::vector lay(mn, 0);
    for (int i = 1; i <= w; i++) {
        for (int j = 0; j < mn; j++) {
            lay[j] = std::max(lay[j], X[i][j]);
        }
    }
    for (int i = 1; i < mn; i++) {
        lay[i] = std::max(lay[i - 1] + 1, lay[i]);
    }

    int q = 0;
    std::cin >> q;
    while (q--) {
        int T = 0, a = 0;
        std::cin >> T >> a;
        a--;
        std::cout << ((idx[a] < mn ? lay[idx[a]] : Inf) > T ? "Yes" : "No") << '\n';
    }
    return;
}

E

解题思路

对于长度为 \(3^n\)\(n > 0\))的一段 01 串,如果我们知道了它三个长度为 \(3^{n - 1}\) 的子串的值和改变这个值所要改变的数量,我们就可以知道答案了。递归着去求就好了。

CODE
std::string s;
// 预处理出 3 的幂
std::vector p3(N + 1, 1);

// 传入左端点和区间长度的次数
std::pair<int, int> dfs(int l, int n) {
    // 递归出口
    if (n == 0) {
        return { s[l] - '0', 1 };
    }

    std::vector son{ dfs(l, n - 1), dfs(l + p3[n - 1], n - 1), dfs(l + 2 * p3[n - 1], n - 1) };
    std::sort(son.begin(), son.end());
    int val = 0, cost = 0;
    if (son[0].first == son[2].first) {
        val = son[0].first;
        int sum = 0, mx = 0;
        for (int i = 0; i < 3; i++) {
            sum += son[i].second;
            mx = std::max(mx, son[i].second);
        }
        cost = sum - mx;
    }
    else {
        val = son[1].first;
        if (val == 0) {
            cost = std::min(son[0].second, son[1].second);
        }
        else {
            cost = std::min(son[1].second, son[2].second);
        }
    }

    return { val, cost };
}

void solve()
{
    int n = 0;
    std::cin >> n;
    std::cin >> s;

    std::cout << dfs(0, n).second << '\n';
}

F

解题思路

先把三个序列都降序排列,定义三元组 \(<i,j,k>\) 的值为 \(V(i, j, k)\),那么我们就知道了最大值是 \(V(0, 0, 0)\)。由于是降序排列,所以有一个很重要的性质:

\[V(i + 1, j, k) \leq V(i, j, k) \And V(i, j + 1, k) \leq V(i, j, k) \And V(i, j, k + 1) \leq V(i, j, k) \]

所以对于第 \(x\) 大的 \(<i, j, k>\),第 \(x + 1\) 大的一定是上面三个中的一个,所以我们就直接优先队列就好了。

下面用了哈希做了三元组和值的互换。

CODE
int n;
std::vector<i64> A(N + 5), B = A, C = A;

i64 f(std::array<int, 3> p) {
    i64 w = 1ll;
    i64 res = 0;
    for (int i = 0; i < 3; i++) {
        res += w * p[i];
        w *= n;
    }
    return res;
}

std::array<int, 3> g(i64 val) {
    std::array<int, 3> res;
    for (int i = 0; i < 3; i++) {
        res[i] = val % n;
        val /= n;
    }
    return res;
}

std::priority_queue<std::pair<i64, i64>> q;
std::set<i64> vis;

void add(std::array<int, 3> p) {
    i64 hp = f(p);
    if (p[0] < n && p[1] < n && p[2] < n && vis.count(hp) == 0) {
        q.push({ A[p[0]] * B[p[1]] + B[p[1]] * C[p[2]] + A[p[0]] * C[p[2]], hp });
        vis.insert(hp);
    }
}

void solve()
{
    int k = 0;
    std::cin >> n >> k;
    for (int i = 0; i < n; i++) {
        std::cin >> A[i];
    }
    for (int i = 0; i < n; i++) {
        std::cin >> B[i];
    }
    for (int i = 0; i < n; i++) {
        std::cin >> C[i];
    }
    std::sort(A.begin(), A.begin() + n, std::greater<i64>());
    std::sort(B.begin(), B.begin() + n, std::greater<i64>());
    std::sort(C.begin(), C.begin() + n, std::greater<i64>());

    q.push({ A[0] * B[0] + B[0] * C[0] + C[0] * A[0], 0 });
    for (int i = 0; i < k; i++) {
        if (i == k - 1) {
            std::cout << q.top().first << '\n';
            break;
        }
        auto [x, y, z] = g(q.top().second);
        q.pop();
        add({ x + 1, y, z });
        add({ x, y + 1, z });
        add({ x, y, z + 1 });
    }
}

G

\(T\) 为我们要构造的串,\(S\) 为给定的串。

使用常规方法求 LCS 的方法我们会用到 \(dp[i][j]\) 表示 \(T[1, i]\)\(S[1, j]\) LCS 的长度是多少。

如果固定 \(i = M\)(即 \(|T|\)),\(dp[i]\)(看成一个数组)就只会有 \(2^N\) 种情况(不严格递增且相邻的数的差最大为 1 ),具体是什么情况取决于 \(T\) 的构造。也就是说如果对于所有可能的 \(dp[M]\) 我们都找到了构造 \(T\) 的方案数,我们就解决了问题。因为我们可以根据 \(dp[M]\) 确定 LCS,具体的,若 \(dp[M][j] - dp[M][j - 1] = 1\),则 \(S_j\) 在 LCS 中,于是也就可以确定 LCS 的长度。

接下来的问题就是求解对于所有可能的 \(dp[M]\),构造 \(T\) 的方案数。将 \(dp[i]\) 看成有 \(2^N\) 种情况的状态 \(u\),我们令 \(f(i, u)\)\(T[1, i]\) 的方案数,这个 \(T[1, i]\)\(S\) 求解 LCS 后产生的动规数组满足 \(dp[i] = u\)(这里 \(i\)\(f(i, u)\) 里面的 \(i\))。于是,\(f(M,u)\) 的意义就是当 \(dp[M] = u\) 时,构造 \(T\) 方案数。

考虑从 \(f(i, u)\) 转移到 \(f(i + 1, u^{'})\):在 \(T[1, i]\) 后加上一个字母得到 \(T[i, i + 1]\),同时状态 \(u\) 要转移到状态 \(u^{'}\)。只要知道了状态 \(u\) 是如何转移得到到状态 \(u^{'}\) 的,我们就解决了 \(f\) 的转移,也就解决了问题。

我们知道在 \(f(i, u)\)\(u[1, j]\) 就是 \(T[1, i]\)\(S[1, j]\) 找 LCS 过程中得到的动规数组,此时若加到 \(T[1, i]\) 后面的字母 \(c\)\(S[j + 1]\) 相同,那么 \(u^{'}[j + 1] = u[j] + 1\),否则 \(u^{'}[j + 1] = max\{u^{'} [j], u[j + 1] \}\)(即满足不递增)。

至此我们就完成了对 \(f(i, u)\) 的转移,但是 \(u\) 作为一个数组并不好作为状态存储,即使最多只有 \(2^N\) 种情况。由于 \(u\) 不降且相邻的数的差最大为 1,所以 \(u\) 的差分数组 \(v\) 是一个 01 数组,我们可以把它压缩成 \(N\) 位二进制数,并将这个由差分得到的二进制数作为状态进行转移,在转移时,我们先通过二进制数得到 \(u\)(前缀),再对得到的 \(u^{'}\) 进行差分。

CODE
void solve()
{
    int n = 0, m = 0;
    std::string s;
    std::cin >> n >> m >> s;
    s = "#" + s;

    std::vector f(m + 1, std::vector(1 << n, 0ll));
    // pre 相当于题解提到的 u
    // dp 相当于题解提到的 u'
    std::vector pre(n + 1, 0), dp(n + 1, 0);
    f[0][0] = 1;
    for (int i = 0; i < m; i++) {
        // 枚举二进制数
        for (int j = 0; j < (1 << n); j++) {
            if (f[i][j] == 0) {
                continue;
            }
            for (int k = 0; k < n; k++) {
                if (j >> k & 1) {
                    pre[k + 1] = pre[k] + 1;
                } else {
                    pre[k + 1] = pre[k];
                }
            }

            for (int c = 0; c < 26; c++) {
                for (int k = 1; k <= n; k++) {
                    if (c == s[k] - 'a') {
                        dp[k] = pre[k - 1] + 1;
                    } else {
                        dp[k] = std::max(pre[k], dp[k - 1]);
                    }
                }
                
                int state = 0; // 对 dp 进行差分的结果
                for (int k = 0; k < n; k++) {
                    if (dp[k] < dp[k + 1]) {
                        state |= 1 << k;
                    }
                }
                (f[i + 1][state] += f[i][j]) %= Mod;
            }
        }
    }

    std::vector ans(n + 1, 0ll);
    for (int u = 0; u < (1 << n); u++) {
        (ans[__builtin_popcount(u)] += f[m][u]) %= Mod;
    }
    for (int i = 0; i <= n; i++) {
        std::cout << ans[i] << " \n"[i == n];
    }
}
posted @ 2025-03-03 22:10  Young_Cloud  阅读(18)  评论(0)    收藏  举报