abc 390 题解

ABC 略

D

首先将 \(N\) 个不同的数分成若干非空组的方案数是贝尔数 \(B(N)\),且 \(B(12) < 5 \times 10^6\),所以我们可以暴力求出所有的组合方案来求解。

CODE
std::unordered_set<i64> vis;
i64 val;
void dfs(int cur, int bags) {
    // 枚举装到第几号袋子里面
    for (int i = 0; i <= bags; i++) {
        val ^= sum[i];
        sum[i] += a[cur];
        val ^= sum[i];
        if (cur == n - 1) {
            vis.insert(val);
        } else {
            dfs(cur + 1, bags + (i == bags));
        }
        val ^= sum[i];
        sum[i] -= a[cur];
        val ^= sum[i];
    }
    return;
}

E

由于每种食物只包含一种维生素,所以对每种维生素单独求出 \(dp[j]\),表示 \(j\) 单位卡路里最多能有的这种维生素的含量。

之后我们贪心地求最大的最小值,具体的:一开始我们每种维生素都分配 0 单位卡路里,我们一单位一单位地分配卡路里,每次分配给现在含量最少的那个维生素。若有相同的就任意选择一个。

CODE
void solve()
{
    int n = 0, x = 0;
    std::cin >> n >> x;
    std::vector val(3, std::vector<std::pair<int, int>>{});
    for (int i = 0; i < n; ++i)
    {
        int a = 0, b = 0, c = 0;
        std::cin >> a >> b >> c;
        val[a - 1].emplace_back(b, c);
    }

    auto work = [&](std::vector<std::pair<int, int>> &v) -> std::vector<int> {
        std::vector dp(x + 1, 0); // dp[i] 表示花费 i 的最大价值
        for (int i = 0; i < v.size(); ++i)
        {
            for (int j = x; j >= v[i].second; --j)
            {
                dp[j] = std::max(dp[j], dp[j - v[i].second] + v[i].first);
            }
        }
        return dp;
    };

    std::vector dp = { work(val[0]), work(val[1]), work(val[2]) };
    std::array<int, 3> id{0, 0, 0};
    for (int i = 0; i < x; ++i)
    {
        int mn = std::min({ dp[0][id[0]], dp[1][id[1]], dp[2][id[2]] });
        for (int j = 0; j < 3; ++j)
        {
            if (dp[j][id[j]] == mn)
            {
                ++id[j];
                break;
            }
        }
    }

    std::cout << std::min({ dp[0][id[0]], dp[1][id[1]], dp[2][id[2]] }) << '\n';
}

F

假设我已经知道了\(f[L, R]\),接下来要求 \(f[L, R + 1]\),且 \(a[R + 1] = x\),则有以下几种情况

  1. 若原序列在 \([L, R]\) 这个区间中不存在 \(\{ x - 1, x, x + 1 \}\) 中的任何数,则 \(f[L, R + 1] = f[L, R]\) + 1。
  2. 若原序列在 \([L, R]\) 这个区间中存在 \(\{ x - 1, x, x + 1 \}\) 中的任意非空非 \(\{ x + 1, x - 1 \}\) 的子集,则 \(f[L, R + 1] = f[L, R]\)
  3. 若原序列在 \([L, R]\) 这个区间中不存在 \(x\),且存在 \(x - 1\)\(x + 1\),则 \(f[L, R + 1] = f[L, R] - 1\)

能否基于以上转移求解从 \(\sum_{i = L}^{R}f(i, R)\)\(\sum_{i = L}^{R + 1}f(i, R + 1)\) 的转移?答案是可行的,但还需要一点观察,就是 \(f(i, R + 1) - f(i, R)\)\(i \in [L, R + 1]\) 是不降的,且值域是 \(\{ -1, 0, 1 \}\),转移的关键就是根据上面所说的几种情况,找到 \(f(i, R + 1) - f(i, R)\) 的分界点。

于是 \(R\)\(1\)\(N\)依次求得 \(\sum_{i = 1}^{R}f(i, R)\),然后答案就是 \(\sum_{R = 1}^{N}\sum_{i = 1}^{R}f(i, R)\)

总的时间复杂度是 \(O(n)\)

CODE
void solve()
{
    int n = 0;
    std::cin >> n;
    std::vector<int> a(n);
    for (auto &i : a) {
        std::cin >> i;
    }
    std::vector<int> p(n + 2, -1);
    std::vector<i64> f(n, 0ll);
    f[0] = 1;
    for (int i = 0; i + 1 < n; i++) {
        p[a[i]] = i;
        int cur = a[i + 1];
        f[i + 1] = 1 + f[i]; // 这里的 1 是 { a[i + 1] }
        // 要 +1 的区间的数量
        f[i + 1] += (i - std::max( {p[cur - 1], p[cur], p[cur + 1] }));

        if (p[cur] < p[cur + 1] && p[cur] < p[cur - 1]) { 
            // 要 -1 的区间的数量
            f[i + 1] -= std::min( {p[cur - 1], p[cur + 1]}) - p[cur];
        }
    }
    std::cout << std::accumulate(f.begin(), f.end(), 0ll) << '\n';
    return;
}
posted @ 2025-03-05 23:29  Young_Cloud  阅读(59)  评论(0)    收藏  举报