wxgmjfhy

atcoder abc390, D-F

目录

abc390

D

  • 直接暴搜时枚举一堆加到另一堆显然不可行

  • 实际上是要把 n 堆石子划分为任意个非空集合 (即贝尔数)

  • 那么暴搜时应该枚举这一堆分到哪个集合

    • 要么分到已有的集合
    • 要么新开一个集合
    for (int t = 1; t <= col; t++) {
        sum[t] += a[i];
        self(self, i + 1, col);
        sum[t] -= a[i];
    }
    
    sum[col + 1] += a[i];
    self(self, i + 1, col + 1);
    sum[col + 1] -= a[i];
    
  • 不过用 set 存答案竟然会 TLE, 用 unordered_set 或者用 vector 并最后去重才能 AC

  • 此外还有遍历二进制状态, 再遍历每个状态的所有子集 (这个过程时间复杂度 $3^n$) 以 DP 的做法

    for (int i = 1; i < (1 << n); i++) {
        int t = __builtin_ctz(i); // 获取二进制最低位的 1
    
        for (int j = i; j; j = (j - 1) & i) {
            if (j >> t & 1) {
                for (auto w : dp[i ^ j]) {
                    dp[i].push_back(w ^ sum[j]);
                }
            }
        }
    
        sort(dp[i].begin(), dp[i].end());
        dp[i].erase(unique(dp[i].begin(), dp[i].end()), dp[i].end());
    }
    
  • 完整代码

#include <bits/stdc++.h>
using namespace std;

int n;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;

    vector<long long> a(n + 1);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    unordered_set<long long> s;
    vector<long long> sum(n + 1);

    auto dfs = [&] (auto self, int i, int col) -> void {
        if (i > n) {
            long long ans = 0;

            for (int t = 1; t <= n; t++) {
                ans ^= sum[t];
            }

            s.insert(ans);
            return;
        }

        for (int t = 1; t <= col; t++) {
            sum[t] += a[i];
            self(self, i + 1, col);
            sum[t] -= a[i];
        }

        sum[col + 1] += a[i];
        self(self, i + 1, col + 1);
        sum[col + 1] -= a[i];
    };

    sum[1] += a[1];
    dfs(dfs, 2, 1);

    cout<< s.size() << "\n";
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

int n;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;

    vector<long long> a(n + 1);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    vector<long long> sum(1 << n);

    for (int i = 1; i < (1 << n); i++) {
        int t = __builtin_ctz(i); // 获取二进制最低位的 1
        sum[i] = sum[i ^ (1 << t)] + a[t + 1];
    }

    vector<vector<long long> > dp(1 << n);
    dp[0].push_back(0);

    for (int i = 1; i < (1 << n); i++) {
        int t = __builtin_ctz(i); // 获取二进制最低位的 1

        for (int j = i; j; j = (j - 1) & i) {
            if (j >> t & 1) {
                for (auto w : dp[i ^ j]) {
                    dp[i].push_back(w ^ sum[j]);
                }
            }
        }

        sort(dp[i].begin(), dp[i].end());
        dp[i].erase(unique(dp[i].begin(), dp[i].end()), dp[i].end());
    }

    cout << dp[(1 << n) - 1].size() << "\n";
    return 0;
}

E

  • 先跑三个背包, 再二分答案就完事了
  • 完整代码
#include <bits/stdc++.h>
using namespace std;

int n, m;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;

    vector<int> v(n + 1), a(n + 1), c(n + 1);

    for (int i = 1; i <= n; i++) {
        cin >> v[i] >> a[i] >> c[i];
    }

    vector<vector<int> > dp(4, vector<int>(m + 1));

    for (int i = 1; i <= n; i++) {
        for (int t = m; t >= c[i]; t--) {
            dp[v[i]][t] = max(dp[v[i]][t], dp[v[i]][t - c[i]] + a[i]);
        }
    }

    auto check = [&] (int x) -> bool {
        long long sum = 0;

        for (int t = 1; t <= 3; t++) {
            int cost = -1;
            for (int i = 1; i <= m; i++) {
                if (dp[t][i] >= x) {
                    cost = i;
                    break;
                }
            }
            if (cost == -1) {
                return false;
            }
            sum += cost;
        }

        return sum <= m;
    };

    int l = 0, r = 1e9;

    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }

    cout << l << "\n";
    return 0;
}

F

  • 每一个区间的值 = 区间内连续段的数目

  • 区间内连续段的数目 = 区间内"数字 x 存在, 但 x+1 不存在"的数目

  • 考虑每个 a[i] 的贡献, 即区间范围包含 i 且区间中不存在 a[i] + 1 的区间的数量

  • 不过注意要避免同种数字的贡献重复计算

    for (int i = 1; i <= n; i++) {
        auto it = lower_bound(pos[a[i] + 1].begin(), pos[a[i] + 1].end(), i);
    
        int l = *prev(it) + 1, r = *it - 1;
        l = max(l, lst[a[i]] + 1); // 避免同种数字的贡献重复计算
    
        if (l <= r) {
            ans += 1ll * (i - l + 1) * (r - i + 1);
        }
    
        lst[a[i]] = i;
    }
    
  • 完整代码

#include <bits/stdc++.h>
using namespace std;

int n;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;

    vector<int> a(n + 1);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    vector<vector<int> > pos(n + 2);

    for (int i = 1; i <= n + 1; i++) {
        pos[i].push_back(0);
    }

    for (int i = 1; i <= n; i++) {
        pos[a[i]].push_back(i);
    }

    for (int i = 1; i <= n + 1; i++) {
        pos[i].push_back(n + 1);
    }

    long long ans = 0;
    vector<int> lst(n + 1);

    for (int i = 1; i <= n; i++) {
        auto it = lower_bound(pos[a[i] + 1].begin(), pos[a[i] + 1].end(), i);

        int l = *prev(it) + 1, r = *it - 1;
        l = max(l, lst[a[i]] + 1); // 避免同种数字的贡献重复计算

        if (l <= r) {
            ans += 1ll * (i - l + 1) * (r - i + 1);
        }

        lst[a[i]] = i;
    }

    cout << ans << "\n";
    return 0;
}