AtCoder Beginner Contest 286
A - Range Swap (abc286 a)
题目大意
给定长度为的数组 和 ,交换 和 并输出交换后的数组 。
解题思路
模拟即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, p, q, r, s; cin >> n >> p >> q >> r >> s; vector<int> a(n); for(auto &i : a) cin >> i; -- p; -- q; -- r; -- s; for(int i = p, j = r; i <= q; ++ i, ++ j) swap(a[i], a[j]); for(auto &i : a) cout << i << ' '; cout << '\n'; return 0; }
B - Cat (abc286 b)
题目大意
给定一个字符串,将其中的替换成
解题思路
模拟即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; string s; cin >> n >> s; string ans; for(int i = 0; i < n; ++ i){ if (s[i] != 'n') ans += s[i]; else if (i + 1 < n && s[i + 1] == 'a'){ ans += "nya"; ++ i; }else ans += s[i]; } cout << ans << '\n'; return 0; }
C - Rotate and Palindrome (abc286 c)
题目大意
给定一个字符串,以及 。可进行如下两种操作若干次:
- 花费 的代价,将字符串的首字母移动到末尾
- 花费 的代价,将字符串的某一位字母替换成任意字母
问将字符串变成回文串的最小代价。
解题思路
观察到这两种操作的执行顺序是可以交换且不影响最终结果的,于是可以先枚举执行第一种操作的次数,之后计算操作二的代价,两者的和取最小值即可。复杂度是
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, a, b; cin >> n >> a >> b; string s; cin >> s; LL ans = 1e18 + 7; auto solve = [&](int st){ int ed = (st - 1 + n) % n; LL tmp = 0; for(int cnt = n / 2; cnt; st = (st + 1) % n, ed = (ed - 1 + n) % n, -- cnt) tmp += 1ll * (s[st] != s[ed]) * b; return tmp; }; for(int i = 0; i < n; ++ i){ ans = min(ans, 1ll * i * a + solve(i)); } cout << ans << '\n'; return 0; }
D - Money in Hand (abc286 d)
题目大意
现在有种硬币,其中第 种硬币价值 元,有 个。
给定,问这些硬币能不能凑出 元。
解题思路
范围都不大,设表示能否凑出 元,转移枚举使用哪种硬币以及使用多少个,一个多重背包随便搞搞就好。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, x; cin >> n >> x; vector<int> dp(x + 1, 0); dp[0] = 1; for(int i = 0; i < n; ++ i){ int a, b; cin >> a >> b; for(int j = x; j >= a; -- j){ for(int k = 1; k <= b; ++ k) if (j >= k * a) dp[j] |= dp[j - k * a]; } } if (dp[x]) cout << "Yes" << '\n'; else cout << "No" << '\n'; return 0; }
E - Souvenir (abc286 e)
题目大意
给定一张有向图,边权均为,给定点权。
有 个询问,每个询问问从点到 点,其边权和最小是多少,在最小的前提下,点权和最大是多少。
不能到达则输出 Impossible
。
解题思路
点数只有,边权和即距离,对原图跑一遍 floyd
即可。后者其实还是可以看成是个距离,在更新边权和时顺便更新点权和即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int inf = 1e9 + 7; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n); for(auto &i : a) cin >> i; vector<string> s(n); for(auto &i : s){ cin >> i; } vector<vector<int>> dis(n, vector<int>(n, inf)); vector<vector<LL>> value(n, vector<LL>(n, 0)); for(int i = 0; i < n; ++ i) for(int j = 0; j < n; ++ j){ if (i == j){ dis[i][j] = 0; value[i][j] = a[i]; } else if (s[i][j] == 'Y'){ dis[i][j] = 1; value[i][j] = a[i] + a[j]; } } for(int k = 0; k < n; ++ k) for(int i = 0; i < n; ++ i) for(int j = 0; j < n; ++ j){ if (dis[i][j] > dis[i][k] + dis[k][j]){ dis[i][j] = dis[i][k] + dis[k][j]; value[i][j] = value[i][k] + value[k][j] - a[k]; }else if (dis[i][j] == dis[i][k] + dis[k][j]){ value[i][j] = max(value[i][j], value[i][k] + value[k][j] - a[k]); } } int q; cin >> q; while(q--){ int u, v; cin >> u >> v; -- u; -- v; if (dis[u][v] == inf) cout << "Impossible" << '\n'; else cout << dis[u][v] << ' ' << value[u][v] << '\n'; } return 0; }
F - Guess The Number 2 (abc286 f)
题目大意
交互题。
你需要猜到对方的的大小,你仅可做一件事:
- 指定,输出一个长度为 的数组。其中
- 对方根据数组 ,输出一个数组
- 你根据该数组 ,得出 的值。
生成数组 的方式为:
- 假想一个有个点的图,其中点连一条有向边到点。
- 的值为:从号点出发,走 条边到达的点的编号。
解题思路
对于一个大小为的环,走一圈需要的代价,因此从对应的数组可以得知的值是多少。
这样从若干个环我们可以得到一组形如 的同余方程。
因此我们就要构造一组相互互质,满足 ,且 ,由中国剩余定理可知在内存在唯一解,且该解可以构造出来。
通过手玩可以构造出一组数组: ,其和为,其乘积为
通过该数组(每个元素就是一个环的大小了)可以构造出对应的数组。然后根据数组计算得到每个余数 ,然后由中国剩余定理解出 。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; vector<LL> a {4, 9, 5, 7, 11, 13, 17, 19, 23}; LL exgcd(LL a, LL b, LL &x, LL &y) { if (b == 0) { x = 1; y = 0; return a; } LL ret = exgcd(b, a % b, y, x); y -= a / b * x; return ret; } LL CRT(vector<LL>& r, vector<LL>& mod) { LL n = 1, ans = 0; int k = r.size(); for (int i = 0; i < k; i++) n = n * mod[i]; for (int i = 0; i < k; i++) { LL m = n / mod[i], b, y; exgcd(m, mod[i], b, y); ans = (ans + r[i] * m * b % n) % n; } return (ans % n + n) % n; } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int sum = accumulate(a.begin(), a.end(), 0); cout << sum << '\n'; int st = 1; vector<LL> out; for(auto &i : a){ int ba = st; ++ st; for(int j = 0; j < i - 1; ++ j){ out.push_back(st); ++ st; } out.push_back(ba); } for(auto &i : out) cout << i << ' '; cout << endl; vector<int> b(sum); for(auto &i : b){ cin >> i; } st = 0; vector<LL> r; for(auto &i : a){ auto pos = find(out.begin() + st, out.begin() + st + i, b[st]); r.push_back(pos - (out.begin() + st) + 1); st += i; } cout << CRT(r, a) << endl; return 0; }
G - Unique Walk (abc286 g)
题目大意
给定一张无向图,以及一个重要边集合。
问是否存在一条路径,其经过了每条重要边仅此一次。
解题思路
如果所有边都是重要边,那题意就是求是否存在一条欧拉通路。
而如果有一些边不重要,即它可以不经过,或经过若干次,那该边所连接的两个点就可以相互到达无限次,我们可以将这两个点缩成一个点。
即将原图里所有不重要的边进行缩边,最后得到一张仅由重要边组成的图,判一下是否存在欧拉通路即可。
无向连通图判断欧拉通路的条件是:仅存在0个或2个度数为奇数的点。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; class dsu { public: vector<int> p; int n; dsu(int _n) : n(_n) { p.resize(n); iota(p.begin(), p.end(), 0); } inline int get(int x) { return (x == p[x] ? x : (p[x] = get(p[x]))); } inline bool unite(int x, int y) { x = get(x); y = get(y); if (x != y) { p[x] = y; return true; } return false; } }; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m; cin >> n >> m; dsu d(n); vector<pair<int, int>> edge(m); for(auto &i : edge){ cin >> i.first >> i.second; -- i.first; -- i.second; } vector<int> s(m, 0); int x; cin >> x; for(int i = 0; i < x; ++ i){ int e; cin >> e; s[e - 1] = 1; } for(int i = 0; i < m; ++ i){ if (!s[i]) d.unite(edge[i].first, edge[i].second); } vector<int> du(n, 0); for(int i = 0; i < m; ++ i){ if (s[i]){ du[d.get(edge[i].first)] ++; du[d.get(edge[i].second)] ++; } } int odd = count_if(du.begin(), du.end(), [](int x){ return (x & 1); }); if (odd == 0 || odd == 2) cout << "Yes" << '\n'; else cout << "No" << '\n'; return 0; }
Ex - Don't Swim (abc286 h)
题目大意
二维平面,给定一个凸多边形,以及在凸多边形外的两个点。问从点 到点 的最短距离,期间不能经过凸多边形内部。
解题思路
看样例有个初步的想法就是其最短路径,一定是由凸多边形的边、点到凸多边形的点,点 到凸多边形的点,这三类边(当然还有个点到点 ,如果不穿过凸多边形)组成的。不可能会平白无故地 到这些点之外的地方。
这样边只有条,跑个最短路就可以了。只是需要判断后两类边会不会与凸多边形有交。
更进一步的,还有一个比较显然的想法就是,如果凸多边形出现在 之间,首先 肯定是从点到凸多边形上,然后绕多边形,然后再到点 。那么对原凸多边形和点 跑一个凸包,那么沿凸包上走,首先不会进入凸多边形内部,同时走的边也是上述这三类。
所以凸包上如果有点 和点 ,那么点 到点 的路径就是凸包上顺时针走或逆时针走两条路径,取个最小值。 而如果没有的话,说明点到点 是畅通无阻的,可以直接走。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; typedef double db; const db EPS = 1e-9; inline int sign(db a) { return a < -EPS ? -1 : a > EPS; } inline int cmp(db a, db b){ return sign(a-b); } struct P { db x, y; P() {} P(db _x, db _y) : x(_x), y(_y) {} P operator+(P p) { return {x + p.x, y + p.y}; } P operator-(P p) { return {x - p.x, y - p.y}; } P operator*(db d) { return {x * d, y * d}; } P operator/(db d) { return {x / d, y / d}; } bool operator<(P p) const { // 字典序小于 int c = cmp(x, p.x); if (c) return c == -1; return cmp(y, p.y) == -1; } bool operator==(P o) const{ // 判断点相等 return cmp(x,o.x) == 0 && cmp(y,o.y) == 0; } db dot(P p) { return x * p.x + y * p.y; } // 点积 db det(P p) { return x * p.y - y * p.x; } // 叉积 db distTo(P p) { return (*this-p).abs(); } // 两点距离 db alpha() { return atan2(y, x); } // 斜率 void read() { cin>>x>>y; } // 读取 void write() {cout<<"("<<x<<","<<y<<")"<<endl;} // 输出 db abs() { return sqrt(abs2());} // 距离原点的距离,向量长度 db abs2() { return x * x + y * y; } // 原点距离平方 P rot90() { return P(-y,x);} // 逆时针旋转90度 P unit() { return *this/abs(); } // 单位向量 int quad() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); } // 是否在0~179度?点在上半边,极角排序用的 P rot(db an){ return {x*cos(an)-y*sin(an),x*sin(an) + y*cos(an)}; } // 逆时针旋转an度 }; // p1->p2 叉积 p1->p3 #define cross(p1,p2,p3) ((p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y)) // p1->p2 叉积 p1->p3 的符号 #define crossOp(p1,p2,p3) sign(cross(p1,p2,p3)) vector<P> convexHull(vector<P> ps) { // 求凸包 int n = ps.size(); if(n <= 1) return ps; sort(ps.begin(), ps.end()); vector<P> qs(n * 2); int k = 0; for (int i = 0; i < n; qs[k++] = ps[i++]) while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k; for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--]) while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k; qs.resize(k - 1); return qs; } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<P> p(n + 2); for(auto &i : p) i.read(); P s = p[n]; P t = p[n + 1]; auto res = convexHull(p); auto ps = find(res.begin(), res.end(), s); auto pt = find(res.begin(), res.end(), t); double ans = 0; if (ps == res.end() || pt == res.end()){ ans = s.distTo(t); }else{ double ans1 = 0, ans2 = 0; for(auto i = ps; i != pt;){ auto nxt = next(i); if (nxt == res.end()) nxt = res.begin(); ans1 += i->distTo(*nxt); i = nxt; } for(auto i = pt; i != ps;){ auto nxt = next(i); if (nxt == res.end()) nxt = res.begin(); ans2 += i->distTo(*nxt); i = nxt; } ans = min(ans1, ans2); } cout << fixed << setprecision(10) << ans << '\n'; return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/17064072.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步