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

1|0A. Antenna Analysis


分类讨论一下

如果xi>xj,则有(xiCi)+(Cjxj)

如果xi<xj,则有(xiCi)+(xj+Cj)

因此我们可以把xj当做下标,(Cjxj),(Cj+xj)当做值,分别实现(xi,),(,xi)的区间最值查询就好了。

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

#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; }

换一种角度来考虑问题,|xy|=max(xy,yx)

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

#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; }

2|0C. Customs Controls


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

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

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

还有一种特殊情况就是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; }

3|0D. 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; }

4|0F. Fortune From Folly


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

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

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

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

#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; }

5|0G. 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; }

6|0J. 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; }

7|0K. 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; }

8|0L. 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; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18396712.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示