AtCoder Beginner Contest 375

1|0A - Seats


#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); int N; cin >> N; string S; cin >> S; int res = 0; for(int i = 0; i + 2 < N; i ++) if(S[i] == '#' and S[i + 2] == '#' and S[i + 1] == '.') res ++; cout << res; return 0; }

2|0B - Traveling Takahashi Problem


#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); int N; cin >> N; double res = 0; int lx = 0, ly = 0; for(int i = 1, x, y; i <= N; i ++) { cin >> x >> y; res += hypot(x - lx, y - ly); lx = x, ly = y; } res += hypot(lx, ly); cout << fixed << setprecision(20) << res; return 0; }

3|0C - Spiral Rotation


通过找规律发现,题目可以这样理解。

把矩形分成N2圈,从外向内,第一圈旋转π2,第二圈旋转2π2,第三圈旋转3π2,第四圈旋转4π2,并以此类推。

知道这个规律的话,我们就可以O(1)计算出每个点被移动到哪里。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); int N; cin >> N; vector A(N + 1, vector<char>(N + 1)); for(int i = 1; i <= N; i ++) for(int j = 1; j <= N; j ++) cin >> A[i][j]; vector B(N + 1, vector<char>(N + 1)); for(int i = 1; i <= N; i ++) { for(int j = 1; j <= N; j ++) { int t = min({i , j , N - i + 1, N - j + 1}); int n = N - 2 * (t - 1), p = t % 4, q = 2 * t - 2; int x = i, y = j; while(p --) { int nx = y, ny = n - x + 1 + q; x = nx, y = ny; } B[x][y] = A[i][j]; } } for(int i = 1; i <= N; i ++){ for(int j = 1;j <= N; j ++){ cout << B[i][j]; } cout << "\n"; } return 0; }

4|0D - ABA


可以分别统计前后缀每种字母出现的次数,然后可以直接枚举出回文串的样子。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); string s; cin >> s; int n = s.size(); vi pre(26), suf(26); for(int i = 2; i < n; i ++) suf[s[i] - 'A'] ++; pre[s[0] - 'A'] ++; int res = 0; for(int i = 1; i + 1 < n; i ++){ for(int j = 0; j < 26; j ++) res += pre[j] * suf[j]; pre[s[i] - 'A'] ++; suf[s[i + 1] - 'A'] --; } cout << res; return 0; }

5|0E - 3 Team Division


这里的换并不是交换,而是可以直接换,因此我们可以直接 dp。

这状态为f[i][x][y]表示前i个人,当前第1队权值和为x,第 2 队权值和为y的最小操作次数。

然后可以直接暴力枚举状态并转移即可。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; const int inf = INT_MAX / 2; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; int sum = 0; vector<pii> p(n); for(auto &[a, b] : p) cin >> a >> b, a --, sum += b; if(sum % 3 != 0) { cout << "-1\n"; return 0; } sum /= 3; vector f(sum + 1, vi(sum + 1, inf)); f[0][0] = 0; for(int pre = 0;auto &[a , b] : p){ pre += b; vector g(sum + 1, vi(sum + 1, inf)); for(int x = 0, y; x < 3; x ++ ) { y = (x != a); for(int i = 0; i <= sum; i ++){ for(int j = 0; j <= sum; j ++){ if(x == 0) { if(i - b >= 0) g[i][j] = min(g[i][j], f[i - b][j] + y); }else if(x == 1){ if(j - b >= 0) g[i][j] = min(g[i][j], f[i][j - b] + y); } else { if(pre - i - j - b >= 0) g[i][j] = min(g[i][j], f[i][j] + y); } } } } f = move(g); } int res = f[sum][sum]; if(res == inf) res = -1; cout << res; return 0; }

6|0F - Road Blocked


我们可以离线,然后倒序做,此时删边操作就变成了加边。

每加入一条边,我们就可以把边所影响的点对进行松弛操作,因为加边的次数不超过300,因此总的复杂度就是 floyd

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; const int inf = LLONG_MAX / 3; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); int n, m, q; cin >> n >> m >> q; vector<array<int,3>> edge(m); for(auto &[u, v, w] : edge) cin >> u >> v >> w; vector<bool> closed(m); vector<vi> query(q); for(int op, x, y; auto &it : query) { cin >> op; if(op == 1) { cin >> x, x --; closed[x] = true; it.push_back(x); } else { cin >> x >> y; it.push_back(x); it.push_back(y); } } vector dis(n + 1, vi(n + 1, inf)); for(int i = 1; i <= n; i ++) dis[i][i] = 0; for(int i = 0; i < m; i ++) { if(closed[i]) continue; const auto &[u, v, w] = edge[i]; dis[u][v] = dis[v][u] = min(dis[u][v], w); } for(int k = 1; k <= n; k ++) for(int i = 1; i <= n; i ++) { if(i == k) continue; for(int j = 1; j < i; j ++) { if(j == k) continue; dis[i][j] = dis[j][i] = min(dis[i][j], dis[i][k] + dis[k][j]); } } ranges::reverse(query); vi res; for(auto it : query) { if(it.size() == 1) { const auto &[u, v, w] = edge[it[0]]; dis[u][v] = dis[v][u] = min(dis[u][v], w); for(int i = 1; i <= n; i ++ ) for(int j = 1; j < i; j ++ ) dis[i][j] = dis[j][i] = min({dis[i][j], dis[i][u] + w + dis[v][j], dis[i][v] + w + dis[u][j]}); } else { int x = it[0], y = it[1]; if(dis[x][y] == inf) res.push_back(-1); else res.push_back(dis[x][y]); } } ranges::reverse(res); for(auto i : res) cout << i << "\n"; return 0; }

7|0G - Road Blocked 2


题目要求的是,哪些边被所有的最短路经过。

首先我们考虑如何求出一条边是否被最短路经过。我们从1求一遍最短路记为d1,从n求一遍最短路记为dn

对于一条边(u,v,w)如果满足d1[u]+w+dn[v]=d1[n]则证明这条边在至少在一条最短路上。

我们在求最短路的过程中,还可以dp 求出从起点到当前点最短路的方案数,以1为起点记为c1,以n为起点记为cn

那么如果c1[u]×cn[v]=c1[n]则说明当前边被所有的最短路经过。

这里的方案数可能分到,我们比较模意义下即可。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; const int inf = LLONG_MAX / 2; const int mod = 998244353; struct mint { int x; mint(int x = 0) : x(x) {} void norm(){ x = (x % mod + mod) % mod; return; } mint &operator=(int o) { return x = o, *this; } mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; } mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; } mint &operator*=(mint o) { return x = (i64) x * o.x % mod, *this; } mint &operator^=(int b) { mint w = *this; mint ret(1); for (; b; b >>= 1, w *= w) if (b & 1) ret *= w; return x = ret.x, *this; } mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); } friend mint operator+(mint a, mint b) { return a += b; } friend mint operator-(mint a, mint b) { return a -= b; } friend mint operator*(mint a, mint b) { return a *= b; } friend mint operator/(mint a, mint b) { return a /= b; } friend mint operator^(mint a, int b) { return a ^= b; } friend bool operator==(mint a, mint b) { a.norm(), b.norm(); return a.x == b.x; } }; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); int n, m, q; cin >> n >> m; vector<array<int,3>> edge(m); vector<vector<pii>> e(n + 1); for(auto &[u, v, w] : edge) { cin >> u >> v >> w; e[u].emplace_back(v, w); e[v].emplace_back(u, w); } auto dij = [=](int s) { vi dis(n + 1, inf), vis(n + 1); vector<mint> cnt(n + 1); dis[s] = 0, cnt[s] = 1;; priority_queue<pii,vector<pii>, greater<>> heap; heap.emplace(0, s); while(not heap.empty()) { auto [d, x] = heap.top(); heap.pop(); if(vis[x]) continue; vis[x] = 1; for(auto [y, w] : e[x]) { if(dis[y] < d + w) continue; if(dis[y] == d + w) { cnt[y] += cnt[x]; continue; } dis[y] = d + w, cnt[y] = cnt[x]; heap.emplace(dis[y], y); } } return pair(dis, cnt); }; auto [d1, c1] = dij(1); auto [dn, cn] = dij(n); for(auto &[u, v, w] : edge){ if(d1[u] + w + dn[v] == d1[n] and c1[u] * cn[v] == c1[n]) cout << "Yes\n"; else if(d1[v] + w + dn[u] == d1[n] and c1[v] * cn[u] == c1[n]) cout << "Yes\n"; else cout << "No\n"; } return 0; }

另一个思路是,我们把所有至少被一条最短路经过的边取出来,建新图。对新图求一下桥即可。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int long long using vi = vector<int>; using pii = pair<int,int>; const int inf = LLONG_MAX / 2; i32 main(){ ios::sync_with_stdio(false), cin.tie(nullptr); int n, m, q; cin >> n >> m; vector<array<int,3>> edge(m); vector<vector<pii>> e(n + 1); for(auto &[u, v, w] : edge) { cin >> u >> v >> w; e[u].emplace_back(v, w); e[v].emplace_back(u, w); } auto dij = [=](int s) { vi dis(n + 1, inf), vis(n + 1); dis[s] = 0; priority_queue<pii,vector<pii>, greater<>> heap; heap.emplace(0, s); while(not heap.empty()) { auto [d, x] = heap.top(); heap.pop(); if(vis[x]) continue; vis[x] = 1; for(auto [y, w] : e[x]) { if(vis[y]) continue; if(dis[y] <= d + w) continue; dis[y] = d + w; heap.emplace(dis[y], y); } } return dis; }; auto d1 = dij(1), dn = dij(n); vector<vector<pii>> g(n + 1); for(int i = 0; i < m; i ++) { const auto &[u, v, w] = edge[i]; if(d1[u] + w + dn[v] == d1[n]) g[u].emplace_back(v, i), g[v].emplace_back(u, i); else if(d1[v] + w + dn[u] == d1[n]) g[u].emplace_back(v, i), g[v].emplace_back(u, i); } vi bridges(m), dfn(n + 1), low(n + 1), fa(n + 1); int cnt = 0; auto tarjan = [&](auto &&tarjan, int x) -> void { low[x] = dfn[x] = ++ cnt; for(auto [y, id]: g[x]) { if(!dfn[y]) { fa[y] = x, tarjan(tarjan, y); low[x] = min(low[x], low[y]); if(low[y] > dfn[x]) bridges[id] = 1; } else if(fa[x] != y) { low[x] = min(low[x], dfn[y]); } } return; }; for(int i = 1; i <= n; i ++) if(!dfn[i]) tarjan(tarjan, i); for(auto i : bridges){ if(i) cout << "Yes\n"; else cout << "No\n"; } return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18462861.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
历史上的今天:
2023-10-13 2021 China Collegiate Programming Contest (CCPC) Guilin Site
2022-10-13 数据结构实验(四)栈与队列的操作与应用
2022-10-13 数据结构(二)线性表
2022-10-13 数字逻辑与数字系统(一)数字逻辑基础
点击右上角即可分享
微信分享提示