2021-2022 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2021)
1|0A. Antenna Analysis
分类讨论一下
如果xi>xj,则有(xi−Ci)+(Cj−xj)
如果xi<xj,则有(−xi−Ci)+(xj+Cj)
因此我们可以把xj当做下标,(Cj−xj),(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;
}
换一种角度来考虑问题,|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;
}
2|0C. 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;
}
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的一的个数大于等k则E(x)=0,否则E(x)=1+(1−p)E(x>>1)+pE((x>>1)|(1<<n−1))
因此我们可以得到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
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
本文链接:https://www.cnblogs.com/PHarr/p/18396712.html
关于博主:前OIer,SMUer
版权声明:CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律