2021-2022 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2021)

A. Antenna Analysis

分类讨论一下

如果\(x_i > x_j\),则有\((x_i - Ci) + (Cj - x_j)\)

如果\(x_i < x_j\),则有\((-x_i - Ci) + (x_j + C_j)\)

因此我们可以把\(x_j\)当做下标,\((Cj - x_j),(Cj + x_j)\)当做值,分别实现\((x_i,\infty),(-\infty,x_i)\)的区间最值查询就好了。

区间最值查询可以使用线段树实现。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

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

const int inf = LLONG_MAX / 2;


struct Node {
    int l, r, value;
    Node *left, *right;

    Node(int l, int r, int value, Node *left, Node *right) :
            l(l), r(r), value(value), left(left), right(right) {};
} *root1, *root2;

// 建树
Node *build(int l, int r) {
    if (l == r) return new Node(l, r, -inf, nullptr, nullptr);
    int mid = (l + r) >> 1;
    Node *left = build(l, mid), *right = build(mid + 1, r);
    return new Node(l, r, max(left->value, right->value), left, right);
}

// 修改
void modify(int l, int r, int v, Node *cur) {
    if (l > cur->r || r < cur->l) return;
    if (l <= cur->l && r >= cur->r) {
        cur->value = max(cur->value, v);
        return;
    }
    int mid = (cur->l + cur->r) >> 1;
    if (l <= mid) modify(l, r, v, cur->left);
    if (r > mid) modify(l, r, v, cur->right);
    cur->value = max(cur->left->value, cur->right->value);
    return;
}

// 查询
int query(int l, int r, Node *cur) {
    if (l <= cur->l && r >= cur->r) return cur->value;
    int mid = (cur->l + cur->r) >> 1, res = -inf;
    if (l <= mid) res = max(res, query(l, r, cur->left));
    if (r > mid) res = max(res, query(l, r, cur->right));
    return res;

}

void modify(int x, int v, Node *cur) {
    modify(x, x, v, cur);
}

int query(int x, Node *cur) {
    return query(1, x, cur);
}


const int N = 2e6;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, c;
    cin >> n >> c;
    root1 = build(-N, N), root2 = build(-N, N);

    for (int i = 1, x, y, z, res; i <= n; i++) {
        cin >> x, res = 0;

        y = x - c * i, z = query(-N, x - 1, root1);
        res = max(res, y + z);
        y = -x - c * i, z = query(x + 1, N, root2);
        res = max(res, y + z);

        modify(x, c * i - x, root1);
        modify(x, c * i + x, root2);
        cout << res << " ";
    }
    return 0;
}

换一种角度来考虑问题,\(|x-y| =\max( x - y , y - x)\)

直接去掉绝对值不会使得答案变大,因此我们可以直接去掉绝对值考虑两种情况的较大值就好了。这样的话,因为少了一个限制条件,直接采用前缀最大值就可以解决了。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

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

const int inf = LLONG_MAX / 2;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, c;
    cin >> n >> c;
    int v1 = -inf, v2 = -inf;
    for (int i = 1, x, res; i <= n; i++) {
        cin >> x;
        v1 = max(v1, c * i - x), v2 = max(v2, c * i + x);
        cout << max(x - c * i + v1, -x - c * i + v2) << " ";
    }
    return 0;
}

C. Customs Controls

我们可以先求两遍最短路,这样就可以判断出哪些边是最短路上的边。

用最短路上的边建一个有向无环图,这个有向无环图一定是从\(1\)出发到\(n\)结束。

对这个有向无环图求拓扑序,把拓扑序的前\(k\)个点设为N剩下的为\(S\)这样在从起点到终点的过程中至多会经历一次颜色变换,所以只要经过至少三个点这种构造方法就是成立的。但是注意,如果最短路是\(1\)\(n\)直接相连的情况,就必须要两个点相同才行。

还有一种特殊情况就是\(n=2,k=1\)是无解的。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

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

const int inf = LLONG_MAX / 2;
const string T = "NS";

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, k;
    cin >> n >> m >> k;
    if (n == 2 and k == 1) {
        cout << "impossible\n";
        return 0;
    }
    vi t(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> t[i];
    vector<vi> e(n + 1);
    for (int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        e[x].push_back(y), e[y].push_back(x);
    }

    auto dij = [&](int sta) {
        vi dis(n + 1, inf), vis(n + 1);
        priority_queue<pii, vector<pii>, greater<>> heap;
        dis[sta] = t[sta], heap.emplace(t[sta], sta);
        while (not heap.empty()) {
            auto [_, x] = heap.top();
            heap.pop();
            if (vis[x]) continue;
            vis[x] = 1;
            for (auto y: e[x]) {
                if (vis[y] or dis[y] <= dis[x] + t[y]) continue;
                dis[y] = dis[x] + t[y];
                heap.emplace(dis[y], y);
            }
        }
        return dis;
    };
    vi d1 = dij(1), dn = dij(n), res(n + 1, -1);


    if (d1[n] == t[1] + t[n]) {

        if (k == 1) res[1] = res[n] = 1;
        else res[1] = res[n] = 0, k -= 2;
        for (int i = 2; i < n; i++) {
            if (k > 0) k--, res[i] = 0;
            else res[i] = 1;
        }
    } else {
        vector<vi> g(n + 1);
        vi deg(n + 1), on(n + 1);
        for (int x = 1; x <= n; x++)
            for (auto y: e[x]) {
                if (d1[x] + dn[y] == d1[n]) {
                    g[x].push_back(y);
                    deg[y]++;
                    on[x] = on[y] = 1;
                }
            }
        vi topo;
        queue<int> q;
        for (int i = 1; i <= n; i++)
            if (on[i] and deg[i] == 0) q.push(i);
        while (not q.empty()) {
            int x = q.front();
            topo.push_back(x);
            q.pop();
            for (auto y: g[x])
                if (--deg[y] == 0) q.push(y);
        }
        topo.pop_back();
        for (auto i: topo) {
            if (k == 0) break;
            res[i] = 0, k--;
        }
        for (int i = 1; i <= n; i++) {
            if (res[i] != -1) continue;
            if (k > 0) res[i] = 0, k--;
            else res[i] = 1;
        }
    }
    for (int i = 1; i <= n; i++)
        cout << T[res[i]];
    return 0;
}

D. Deceptive Directions

首先因为题目要走的是最短路,我们可以先一遍搜索找到最短路,然后第二遍搜索的时候沿着最短走,且每次枚举三个方向就好了。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

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

const int inf = LLONG_MAX / 2;

const int N = 20;
const double eps = 0.01;


const vi dx = {-1, 1, 0, 0}, dy = {0, 0, -1, 1};

int sgn(char c) {
    if (c == 'N') return 0;
    else if (c == 'S') return 1;
    else if (c == 'W') return 2;
    else return 3;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> m >> n;
    vector<string> g(n);
    for (auto &i: g)
        cin >> i;
    string I;
    cin >> I;
    int sx = -1, sy = -1;
    for (int i = 0; i < n and sx == -1 and sy == -1; i++)
        for (int j = 0; j < m and sx == -1 and sy == -1; j++)
            if (g[i][j] == 'S') sx = i, sy = j;

    queue<pii> q;
    q.emplace(sx, sy);
    vector vis(n, vi(m)), dis(n, vi(m, inf));
    dis[sx][sy] = 0;
    while (not q.empty()) {
        auto [x, y] = q.front();
        q.pop();
        if (vis[x][y]) continue;
        vis[x][y] = 1;
        for (int i = 0, fx, fy; i < 4; i++) {
            fx = x + dx[i], fy = y + dy[i];
            if (fx < 0 or fy < 0 or fx >= n or fy >= m) continue;
            if (g[fx][fy] == '#') continue;
            if (vis[fx][fy] or dis[fx][fy] <= dis[x][y] + 1) continue;
            dis[fx][fy] = dis[x][y] + 1;
            q.emplace(fx, fy);
        }
    }

    vis = vector(n, vi(m));
    q.emplace(sx, sy);
    while (not q.empty()) {
        auto [x, y] = q.front();
        q.pop();
        if (vis[x][y]) continue;
        vis[x][y] = 1;
        if (dis[x][y] == I.size()) {
            g[x][y] = '!';
            continue;
        }
        for (int i = 0, t = sgn(I[dis[x][y]]), fx, fy; i < 4; i++) {
            if (i == t) continue;
            fx = x + dx[i], fy = y + dy[i];
            if (fx < 0 or fy < 0 or fx >= n or fy >= m) continue;
            if (g[fx][fy] == '#') continue;
            if (vis[fx][fy] or dis[fx][fy] != dis[x][y] + 1) continue;
            q.emplace(fx, fy);
        }
    }
    for (auto i: g)
        cout << i << "\n";

    return 0;
}

F. Fortune From Folly

我们用最后一个\(n\)位的二进制表示最后\(n\)个打开的箱子。

\(E(x)\)表示\(x\)状态到最终状态的期望步数。

如果\(x\)的一的个数大于等\(k\)\(E(x) = 0\),否则\(E(x) = 1 + (1 - p ) E( x >> 1) + pE((x >> 1) | (1 << n - 1) )\)

因此我们可以得到\(2^n\)个方程和\(2^n\)个未知数。高斯消元解方程就好了。

#include <bits/stdc++.h>

using namespace std;

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


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

const int inf = LLONG_MAX / 2;

const int M = 100;
ldb a[M][M], b[M], X[M];


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    ui32 n, k, t, N;
    ldb p;

    cin >> n >> k >> p;
    N = 1 << n, t = N >> 1;
    for (ui32 x = 0, y, z; x < N; x++) {
        if (popcount(x) >= k) {
            a[x][x] = 1;
        } else {
            y = x >> 1, z = y | t;
            assert(y < N and z < N);
            a[x][x] = 1;
            a[x][y] -= 1 - p;
            a[x][z] -= p;
            b[x] = 1;
        }
    }


    for (int i = 0; i < N; ++i) {
        for (int j = i + 1; j < N; ++j) {
            for (int k = i + 1; k < N; ++k) {
                a[j][k] -= a[i][k] / a[i][i] * a[j][i];
            }
            b[j] -= b[i] / a[i][i] * a[j][i];
            a[j][i] = 0;
        }
    }
    for (int i = N - 1; i >= 0; --i) {
        assert(a[i][i] != 0);
        X[i] = b[i] / a[i][i];
        for (int j = i - 1; j >= 0; --j) {
            b[j] -= a[j][i] * X[i];
        }
    }
    cout << fixed << setprecision(20) << X[0];
    return 0;
}

G. Grazed Grains

精度要求很低,直接枚举点就好

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

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

const int inf = LLONG_MAX / 2;

const int N = 20;
const double eps = 0.01;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vector<double> ox(n), oy(n), r(n);
    for (int i = 0; i < n; i++)
        cin >> ox[i] >> oy[i] >> r[i], r[i] = r[i] * r[i];
    cerr << "Sss\n";
    double res = 0;
    for (double x = -N; x <= N; x += eps)
        for (double y = -N; y <= N; y += eps) {
            bool flag = false;
            for (int i = 0; i < n and flag == false; i++) {
                double d = (x - ox[i]) * (x - ox[i]) + (y - oy[i]) * (y - oy[i]);
                if (d <= r[i]) flag = true;
            }
            if (flag) res += eps * eps;
        }
    cout << fixed << setprecision(20) << res;

    return 0;
}

J. Joint Jog Jam

手画一下各种情况就会发现,最远距离一定出现起点或终点

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

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

const int inf = LLONG_MAX / 2;

const int N = 2e6;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int sx1, sx2, ex1, ex2, sy1, sy2, ey1, ey2;
    cin >> sx1 >> sy1 >> sx2 >> sy2 >> ex1 >> ey1 >> ex2 >> ey2;
    int sd = (sx1 - sx2) * (sx1 - sx2) + (sy1 - sy2) * (sy1 - sy2);
    int ed = (ex1 - ex2) * (ex1 - ex2) + (ey1 - ey2) * (ey1 - ey2);
    int d = max(sd, ed);
    cout << fixed << setprecision(20) << sqrt(d);
    return 0;
}

K. Knot Knowledge

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

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


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vi a(n), b(n - 1);
    for (auto &i: a) cin >> i;
    for (auto &i: b) cin >> i;
    ranges::sort(a), ranges::sort(b);
    for (int i = 0; i < n - 1; i++) {
        if (a[i] != b[i]) {
            cout << a[i];
            return 0;
        }
    }
    cout << a.back();

    return 0;
}

L. Locust Locus

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

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

const int inf = INT_MAX / 2;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int k;
    cin >> k;
    int res = inf;
    for (int i = 0, y, c1, c2; i < k; i++) {
        cin >> y >> c1 >> c2;
        res = min(res, y + lcm(c1, c2));
    }
    cout << res;


    return 0;
}
posted @ 2024-09-04 16:01  PHarr  阅读(57)  评论(0)    收藏  举报