2024 Xiangtan University Summer Camp-Div.2

A. 二度树上的染色游戏

因为题目保证了是二叉树,所以每次至多只需要选择一个子节点染成红色。所以可以贪心的选择红色权值小的子树即可。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using ldb = long double;

const i32 inf = INT_MAX / 2;
const i64 INF = LLONG_MAX / 2;

#define int i64

using vi = vector<int>;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vi a(n + 1);
    int sum = 0;
    for (int i = 1; i <= n; i++)
        cin >> a[i], sum += a[i];
    vector<vi> e(n + 1);
    for (int i = 1, u, v; i < n; i++) {
        cin >> u >> v;
        e[u].push_back(v), e[v].push_back(u);
    }
    auto dfs = [&](auto &&self, int x, int fa) -> int {
        int cnt = 0;
        for (int y: e[x])
            cnt += y != fa;
        if (cnt < 2) return a[x];
        int ans = inf;
        for (int y: e[x]) {
            if (y == fa) continue;
            ans = min(ans, self(self, y, x));
        }
        return ans + a[x];
    };
    int red = dfs(dfs, 1, -1);
    cout << sum - red - red;
    return 0;
}

B. 小文的排列

我们可以把序列分层若干个子段,这样的话,除了开头的子段和结尾的子段,剩下的字段必须都是完整的\(m\)的排列,并且开头和结尾必须是\(m\)排列的子段。

因此我们可以用双指针扫描出所有的合法子段,然后做一个简单的dp 就好了。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using ldb = long double;

const i32 inf = INT_MAX / 2;
const i64 INF = LLONG_MAX / 2;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vi a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    if (ranges::max(a) > m) {
        cout << "NO\n";
        return 0;
    }
    vector<vi> e(n + 1);
    set<int> vis;
    for (int l = 1, r = 0; l <= n; l++) {
        while (r + 1 <= n and vis.count(a[r + 1]) == 0) {
            r++, vis.insert(a[r]);
            if (l == 1) e[r].push_back(1);
        }
        if (r == n or vis.size() == m)
            e[r].push_back(l);
        vis.erase(a[l]);
    }
    vi f(n + 1);
    f[0] = 1;
    for (int r = 1; r <= n; r++)
        for (int l: e[r])
            if (f[l - 1]) {
                f[r] = 1;
                break;
            }

    if (f[n]) cout << "YES";
    else cout << "NO";
    return 0;
}

C. gcd hard version

区间最大公约数可以用ST表求,区间和可以用前缀和求。

对于当前的区间\([l,r]\),如果\(r\)变大,最大公约数是不增的,区间和是不减的,因此如果\([l,r]\)满足,\([l,r+1]\)一定满足。

所以我们可以枚举左端点,然后直接二分出右端点。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using ldb = long double;

const i32 inf = INT_MAX / 2;
const i64 INF = LLONG_MAX / 2;

#define int i64

using vi = vector<int>;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    int lg2N = log2(n);
    vi lg2(n + 1);
    vector f(n + 1, vi(lg2N + 1));
    lg2[0] = -1;
    for (int i = 1; i <= n; i++) {
        cin >> f[i][0];
        lg2[i] = lg2[i / 2] + 1;
    }
    for (int j = 1; j <= lg2N; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            f[i][j] = gcd(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
    auto query = [=](int l, int r) {
        assert(l <= r);
        int s = lg2[r - l + 1];
        return gcd(f[l][s], f[r - (1 << s) + 1][s]);
    };

    vi pre(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> pre[i];
        pre[i] += pre[i - 1];
    }
    int res = 0;
    for (int i = 1; i <= n; i++) {
        int l = i, r = n, ans = -1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (query(i, mid) <= pre[mid] - pre[i - 1]) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        if (ans == -1) continue;
        res += n - ans + 1;
    }
    cout << res;
    return 0;
}

D. 战至终章

挑战的顺序一定是一种拓扑序。有多个可以挑战的情况下挑战\(a\)最小的一定最优。因此我们在求拓扑序的时候使用优先队列即可。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using ldb = long double;

const i32 inf = INT_MAX / 2;
const i64 INF = LLONG_MAX / 2;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, p;
    cin >> n >> p;
    vi a(n + 1), b(n + 1), deg(n + 1);
    vector<vi> e(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    priority_queue<pii> heap;
    for (int i = 1; i <= n; i++) {
        cin >> deg[i];
        if (deg[i] == 0) heap.emplace(-a[i], i);
        for (int j = 0, x; j < deg[i]; j++)
            cin >> x, e[x].push_back(i);
    }
    vi res;
    while (not heap.empty()) {
        auto [_, x] = heap.top();
        heap.pop();
        if (a[x] > p) continue;
        res.push_back(x), p += b[x];
        for (auto y: e[x])
            if (--deg[y] == 0) {
                heap.emplace(-a[y], y);
            }
    }
    ranges::sort(res);
    cout << res.size() << "\n";
    for (auto i: res) cout << i << " ";

    return 0;
}
posted @ 2024-09-04 19:35  PHarr  阅读(19)  评论(0编辑  收藏  举报