Grakn Forces 2020

比赛链接:https://codeforces.com/contest/1408

A. Circle Coloring

题意

给出三个长为 $n$ 的序列 $a,b,c$,对于每个 $i$,$a_i \ne b_i,\ a_i \ne c_i,\ b_i \ne c_i$ 。

构造序列 $p$,使得:

  • $p_i \in \{a_i, b_i, c_i\}$
  • $p_i \neq p_{(i + 1 \mod n)}$

题解

即每个数不与前后两个数相同,因为三个序列同一位置两两不同,所以对于每个位置一定都有满足条件的数,枚举即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<vector<int>> vec(3, vector<int> (n));
        for (auto &v : vec) {
            for (auto &x : v) {
                cin >> x; 
            }
        }
        vector<int> p(n);
        for (int i = 0; i < n; i++) {
            for (auto &v : vec) {
                if (v[i] != p[(i - 1 + n) % n] and v[i] != p[(i + 1) % n]) {
                    p[i] = v[i];
                    break;
                }
            }
        }
        for (int i = 0; i < n; i++) {
            cout << p[i] << " \n"[i == n - 1];
        }
    }
    return 0;
}

B. Arrays Sum

题意

给出一个大小为 $n$ 的非递减序数组 $a$ 和一个正整数 $k$ 。

构造 $m$ 个非递减序数组 $b_1, b_2, \ldots, b_m$,要求:

  • 每个 $b_i$ 大小为 $n$
  • $a_i = b_{1, i} + b_{2, i} + \ldots + b_{m, i}$
  • 每个 $b_i$ 中最多有 $k$ 个不同的数 

找出 $m$ 的最小值。

题解

设 $a$ 中有 $x$ 个不同的数,答案即 $1 + \lceil \frac{x - k}{k - 1} \rceil$ 。

因为 $b_1$ 可以包含 $a$ 中前 $k$ 个不同的数,之后的 $b_i$ 因为要用 $0$ 维护前面已有的和,每次只能新加 $a$ 中 $k-1$ 个不同的数。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n, k;
        cin >> n >> k;
        set<int> st;
        for (int i = 0; i < n; i++) {
            int x;
            cin >> x;
            st.insert(x);
        }
        if (k == 1) {
            cout << (st.size() == 1 ? 1 : -1) << "\n";
            continue;
        }
        cout << 1 + max(0, (int(st.size()) - 2)) / (k - 1) << "\n";
    }
    return 0;
}

C. Discrete Acceleration

题意

有一条公路从坐标 $0$ 到坐标 $l$ 长为 $l$ 米,初始时甲车在坐标 $0$ 处,乙车在坐标 $l$ 处。

公路上有 $n$ 个加速点,两车初速度均为 $1m/s$,经过一个加速点增加 $1m/s$,计算两车多久相遇。

题解

计算两车到每个加速点的时间,两车会在甲更快到的加速点和乙更快到的加速点之间相遇。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);
    int t;
    cin >> t;
    while (t--) {
        int n, l;
        cin >> n >> l;
        vector<double> a(n + 2);
        a[0] = 0;
        a[n + 1] = l;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        vector<double> t1(n + 2), t2(n + 2);
        t1[0] = 0;
        t2[n + 1] = 0;
        for (int i = 1; i < n + 2; i++) {
            t1[i] = t1[i - 1] + (a[i] - a[i - 1]) / i;
        }
        for (int i = n; i >= 0; i--) {
            t2[i] = t2[i + 1] + (a[i + 1] - a[i]) / (n + 1 - i);
        }
        double ans = 0.0;
        for (int i = 1; i < n + 2; i++) {
            if (t1[i - 1] <= t2[i - 1] and t1[i] >= t2[i]) {
                ans = max(t1[i - 1], t2[i]) + (a[i] - a[i - 1] - abs(t1[i - 1] - t2[i]) * (t1[i - 1] < t2[i] ? i : n + 2 - i)) / (n + 2);
                break;
            }
        }
        cout << ans << "\n";
    }
    return 0;
}

D. Searchlights

题意

在二维平面上给出 $n$ 个人和 $m$ 个探照灯的坐标 $(a,b)$ 和 $(c,d)$,每次操作可以选择:

  • 将所有人横坐标加一
  • 将所有人纵坐标加一

问使得任一人都不在任一探照灯左下方的最少操作次数。

题解

对于第 $i$ 个人和第 $j$ 个探照灯 ,如果 $dx + a_i \ge c_j + 1$,那么他一定可以移出探照灯的范围,否则,对于所有小于 $dx = c_j - a_i + 1$ 的移动次数,它们对应的纵坐标移动次数至少要为 $d_j - b_i + 1$ 次才能移出该探照灯的范围。

对于每个人枚举 $m$ 个探照灯,记录并更新每个横坐标移动次数对应的纵坐标移动次数的最大值,最后从后向前遍历并更新纵坐标的最大移动次数,因为如果之前移动了更多的横坐标仍需将纵坐标移动这么多次,那么对于现在移动了更少的横坐标也是必须的。

$dp_i$ 的含义为横坐标移动次数为 $i$ 时,纵坐标的移动次数至少要为 $dp_i$ 才能保证所有人都移出探照灯的范围。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i] >> b[i];
    }
    vector<int> c(m), d(m);
    for (int i = 0; i < m; i++) {
        cin >> c[i] >> d[i];
    }
    constexpr int N = 1e6 + 10;
    vector<int> dp(N);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (a[i] <= c[j]) {
                dp[c[j] - a[i]] = max(dp[c[j] - a[i]], d[j] + 1 - b[i]);
            }
        }
    }
    for (int i = N - 2; i >= 0; i--) {
        dp[i] = max(dp[i], dp[i + 1]);
    }
    int ans = INT_MAX;
    for (int i = 0; i < N; i++) {
        ans = min(ans, i + dp[i]);
    }
    cout << ans << "\n";
    return 0;
}

E. Avoid Rainbow Cycles

题意

给出 $m$ 个集合 $A$,所有元素大小在 $[1,n]$ 之间。

每个集合中的元素两两成边,颜色与集合相同,不同集合颜色不同。

给出大小为 $m,n$ 的两个数组 $a,b$,从 $A_i$ 中移除值为 $j$ 的元素花费为 $a_i + b_j$ 。

彩虹环指边颜色不重复的环,问使得图中无彩虹环的最少花费。 

题解

构造集合和数值的二分图,$A_i$ 与值为 $j$ 的元素间边权为 $a_i + b_j$,最少花费即为总边权和减去二分图的最大生成树。

代码

#include <bits/stdc++.h>
using namespace std;
constexpr int N = 2e5 + 10;

int fa[N];

int Find(int x) {
    return fa[x] == x ? x : fa[x] = Find(fa[x]);
}

void Union(int x, int y) {
    x = Find(x);
    y = Find(y);
    if (x != y) {
        if (x < y) fa[y] = x;
        else fa[x] = y;
    }
}

void Init() {
    for (int i = 0; i < N; i++) {
        fa[i] = i;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    Init();
    int m, n;
    cin >> m >> n;
    vector<int> a(m);
    for (auto &x : a) cin >> x;
    vector<int> b(n);
    for (auto &x : b) cin >> x;
    long long sum = 0;
    vector<tuple<int, int, int>> edges; 
    for (int i = 0; i < m; i++) {
        int s;
        cin >> s;
        for (int j = 0; j < s; j++) {
            int x;
            cin >> x;
            --x;
            sum += a[i] + b[x];
            edges.emplace_back(a[i] + b[x], i + n, x);
        }
    }
    sort(edges.begin(), edges.end(), greater<>());
    for (auto [w, u, v] : edges) {
        if (Find(u) != Find(v)) {
            Union(u, v);
            sum -= w;
        }
    }
    cout << sum << "\n";
    return 0;
}

F. Two Different

题意

给出一个大小为 $n$ 的数组 $a$,其中 $a_i = i$ 。

每次操作可以选取两个元素并用二元映射函数 $f$ 赋值:$a_i = a_j = f(a_i,a_j)$ 。

试给出一种操作方式,使得无论映射函数如何,最终 $a$ 中最多只有两个不同的数。

题解

如果数组大小为 $2^p$,那么最终可以都变为一个数。

如:

  1. 1 2 3 4
  2. 5 5 6 6
  3. 7 7 7 7

如果数组大小不为 $2^p$,对两个端点所在的 $2^p$ 长区间各操作一次即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<pair<int, int>> ans;
    function<void(int, int)> dfs = [&](int l, int r) {
        if (l == r) return;
        int mid = (l + r) / 2;
        dfs(l, mid);
        dfs(mid + 1, r);
        for (int i = l, j = mid + 1; i <= mid; i++, j++) {
            ans.emplace_back(i, j);
        }
    };
    int p = 1 << __lg(n);
    dfs(1, p);
    if (p != n) {
        dfs(n - p + 1, n);
    }
    cout << ans.size() << "\n";
    for (auto [x, y] : ans) {
        cout << x << ' ' << y << "\n";
    }
    return 0;
}

参考博客

https://blog.csdn.net/qq_45458915/article/details/108912813

posted @ 2020-10-04 23:20  Kanoon  阅读(178)  评论(0编辑  收藏  举报