AtCoder Beginner Contest 289
A - flip (abc289 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); string s; cin >> s; for(auto &i : s){ cout << ((i - '0') ^ 1); } return 0; }
B - V (abc289 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, m; cin >> n >> m; int st = 1, ed = 1; for(int i = 0; i < m; ++ i){ int a; cin >> a; while(ed < a){ for(int j = ed; j >= st; -- j) cout << j << ' '; ++ ed; st = ed; } ++ ed; } while(ed <= n){ for(int j = ed; j >= st; -- j) cout << j << ' '; ++ ed; st = ed; } return 0; }
C - Coverage (abc289 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, m; cin >> n >> m; vector<bitset<10>> qwq(m); for(auto &i : qwq){ int c; cin >> c; while(c--){ int x; cin >> x; i.set(x - 1); } } int ok = (1 << n) - 1; bitset<10> good; int ans = 0; for(int i = 1; i < (1 << m); ++ i){ good.reset(); for(int j = 0; j < m; ++ j){ if ((i >> j) & 1) good |= qwq[j]; } ans += (good.to_ulong() == ok); } cout << ans << '\n'; return 0; }
D - Step Up Robot (abc289 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; cin >> n; vector<int> op(n); for(auto &i : op) cin >> i; int m; cin >> m; vector<int> danger(m); for(auto &i : danger) cin >> i; int x; cin >> x; vector<int> dp(x + 1, 0); vector<int> ok(x + 1, 1); for(auto &i : danger) ok[i] = 0; dp[0] = 1; for(int i = 1; i <= x; ++ i){ if (!ok[i]) continue; for(auto &j : op){ if (i >= j) dp[i] |= dp[i - j]; } } if (dp[x]) cout << "Yes" << '\n'; else cout << "No" << '\n'; return 0; }
E - Swap Places (abc289 e)
题目大意
给定一张个点条边的无向图,点有红蓝两种颜色。
高橋从号点出发,青木从 号点出发。
每个时刻,两人同时移动至其相邻点,要求每次移动之后,两人所在点的颜色不同。
问两人能否同时抵达号点和 号点,若能的话,输出最小耗时。
解题思路
考虑爆搜(也算是dp?),设表示高橋抵达号点出发,青木抵达号点所需要的最小时刻。然后枚举两人下一个抵达的点,BFS。
分析其复杂度,发现其惊奇地可过,于是就做完了(
该搜索,状态总共有,但每个状态的转移代价都不是固定的,我们考虑所有转移的代价。
转移的代价来自于边
的遍历,对于一条边来说,它最多可被遍历次(对于青木走的每条边,我们每考虑一条高橋走的边,该边就会被遍历一次),因此所有边的遍历次数和为,这即为转移的代价。
因为我们考虑了转移代价的和,而不是每个状态的转移代价,因此总的复杂度应为状态代价+转移代价。
因此该搜索的时间复杂度为 ,而,是可以通过的。
神奇的代码
#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 t; cin >> t; while(t--){ int n, m; cin >> n >> m; vector<int> color(n); for(auto &i : color) cin >> i; vector<vector<int>> edge(n); for(int i = 0; i < m; ++ i){ int u, v; cin >> u >> v; -- u; -- v; edge[u].push_back(v); edge[v].push_back(u); } auto bfs = [&](int l, int r){ queue<pair<int, int>> team; vector<vector<int>> dis(n, vector<int>(n, inf)); dis[l][r] = 0; team.push({l, r}); while(!team.empty()){ auto [u, v] = team.front(); team.pop(); for(auto x : edge[u]){ for(auto y : edge[v]){ if (color[x] != color[y] && dis[x][y] > dis[u][v] + 1){ dis[x][y] = dis[u][v] + 1; team.push({x, y}); } } } } return dis[n - 1][0]; }; int ans = bfs(0, n - 1); if (ans == inf) ans = -1; cout << ans << '\n'; } return 0; }
F - Teleporter Takahashi (abc289 f)
题目大意
二维平面,要求从点到点 。
给定一个矩形区域,每次操作从区域里选择一个点,然后点 就跑到与 点的镜像点。
给定一个操作序列,或告知不可能。不要求最小化操作次数。
解题思路
考虑一维情况。
如果区域是一个点,很显然点只有两个位置可以选择。
如果区域不是一个点,假设是。既然不要求最小化操作次数,那么考虑是否有基本的移动操作。
可以观察到,两次操作分别选择点和点 ,那么当前点 相对操作前,坐标 了。反之选择点 和点 就会 。那就可以依靠此基本操作
一步一步移向终点。由坐标范围限制,操作次数不会超过上限。
注意到,原点坐标,选择点坐标 ,那镜像点坐标的奇偶性和 相同。故操作不会改变坐标奇偶性。因此如果起点和终点的奇偶性不同,则不可达。
一维解决了,二维的话两个维度是相互独立的,因此分别考虑两个维度移动即可。注意区域变成一个点或一条线的情况。
神奇的代码
#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 sx, sy, tx, ty, a, b, c, d; cin >> sx >> sy >> tx >> ty >> a >> b >> c >> d; vector<pair<int, int>> ans; auto move = [&](int x, int y){ sx = 2 * x - sx; sy = 2 * y - sy; ans.push_back({x, y}); }; auto check = [&](){ bool ok0 = ((sx ^ tx) % 2 == 0 && (a != b || (sx == tx || a + b == sx + tx))); bool ok1 = ((sy ^ ty) % 2 == 0 && (c != d || (sy == ty || c + d == sy + ty))); if (!ok0 || !ok1) return false; if (a == b && sx != tx){ move(a, c); } if (c == d && sy != ty){ move(a, c); } if (a == b && sx != tx) return false; while(sx < tx){ move(a, c); move(a + 1, c); } while(sx > tx){ move(a + 1, c); move(a, c); } while(sy < ty){ move(a, c); move(a, c + 1); } while(sy > ty){ move(a, c + 1); move(a, c); } return true; }; if (check()){ cout << "Yes" << '\n'; for(auto &i : ans) cout << i.first << ' ' << i.second << '\n'; }else { cout << "No" << '\n'; } return 0; }
G - Shopping in AtCoder store (abc289 g)
题目大意
给定个数 ,有 个询问,每个询问给定一个 ,问最大的 ,使得 最大。输出该乘积最大值。
其中 表示满足的 的数量。
解题思路
很显然是个分段函数,因此其选择的肯定满足:存在某个有。
首先对降序排序,对于第 个(从开始) ,如果它是那个满足 的话,则 。这里是个常数,变数是 。
有个感性的观察就是 越大的时候,越后面的 成为答案的可能性就越可能大。且如果成为 的答案,那么对于 的答案,肯定满足 (决策单调),但因为存在 并不意味着 ,所以也无法解决。
对问题更进一步的抽象,每个就是 ,其中 就是选择其时的答案, 是定值,只有 变量,也就是一个一次函数 。给定一个 ,其实就是带入所有的一次函数,取最大的
斜率越大的在越大时越有可能成为最大值。因此一次函数在成为最大值对应的 区间要么只有一个,要么就没有。
由此我们可以维护一个由这些一次函数做围成的一个凸包,凸包上的边就是取最大值的那个一次函数的部分。
如何维护呢?
首先注意到,给定两个斜率不同的一次函数,它们有一个交点,交点前是斜率小的函数值较大,交点后是斜率大的函数值较大
因此对于此时凸包上最末尾的一次函数,次末尾的是,一个新加入的一次函数 ,与 的交点 ,与 的交点,如果交点的横坐标,,那么就没用了(会比更早的( 超过成为最大)。
通过解方程,交点的横坐标,则 和 的比较,通过把分母移动到另一边就变成跟求凸包时判断点 差积一样的形式 。
得到凸包后对于一个,二分找到其所在的一次函数,代入其值求得答案即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; using db = double; 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(LL _x, LL _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 inline db cross(const P& p1, const P& p2, const P& p3) { return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } // p1->p2 叉积 p1->p3 的符号 inline int crossOp(const P& p1, const P& p2, const P& p3) { return sign(cross(p1,p2,p3)); } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m; cin >> n >> m; vector<LL> b(n); for(auto &i : b) cin >> i; sort(b.begin(), b.end(), greater<LL>()); vector<pair<double, P>> l; for(int i = 0; i < n; ++ i){ P p{i + 1, (i + 1) * b[i]}; while(l.size() >= 2 && cross(p, (l.rbegin()->second), (next(l.rbegin()))->second) <= 0) l.pop_back(); db x = 0; if (!l.empty()){ P p1 = l.back().second; x = (p1.y - p.y) / (p.x - p1.x); } l.push_back({x, p}); } for(int i = 0; i < m; ++ i){ int c; cin >> c; P p = prev(upper_bound(l.begin(), l.end(), make_pair(1.0 * c, P{0.0,0.0}), [](const auto& a, const auto& b){ return a.first < b.first; }))->second; LL ans = p.x * c + p.y; cout << ans << ' '; } return 0; }
Ex - Trio (abc289 h)
题目大意
三个人在数轴上随机游走,问时刻三者第一次相遇的概率。
随机游走指每个时刻,等概率往左走一个或往右走一个。
概率对取模。
解题思路
<++>
神奇的代码
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/17113815.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步