AtCoder Beginner Contest 360
A - A Healthy Breakfast (abc360 A)
题目大意
给定一个字符串包含RMS
,问R
是否在S
的左边。
解题思路
比较R
和S
的下标,谁小即谁在左边。
神奇的代码
#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; cout << (s.find('R') < s.find('M') ? "Yes" : "No") << '\n'; return 0; }
B - Vertical Reading (abc360 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); string s, t; cin >> s >> t; bool ok = false; for (int i = 1; i < s.size(); i++) { vector<string> sub; for (int j = 0; j < s.size(); j += i) { sub.push_back(s.substr(j, i)); } for (int j = 0; j < i; ++j) { string ans; for (auto& x : sub) { if (j < x.size()) ans += x[j]; } if (ans == t) { ok = true; } } } if (ok) cout << "Yes" << '\n'; else cout << "No" << '\n'; return 0; }
C - Move It (abc360 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; cin >> n; vector<vector<int>> box(n); vector<int> pos(n); for (auto& i : pos) cin >> i; for (int i = 0; i < n; ++i) { int w; cin >> w; box[pos[i] - 1].push_back(w); } int ans = 0; for (auto& i : box) { if (i.empty()) continue; sort(i.begin(), i.end(), greater<int>()); ans += accumulate(i.begin(), i.end(), 0) - i[0]; } cout << ans << '\n'; return 0; }
D - Ghost Ants (abc360 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, t; string dir; cin >> n >> t >> dir; array<vector<int>, 2> pos; for (int i = 0; i < n; ++i) { int p; cin >> p; pos[dir[i] - '0'].push_back(p); } for (auto& x : pos) sort(x.begin(), x.end()); LL ans = 0; for (int i = 1; i < 2; ++i) { auto& cur = pos[i]; auto& nxt = pos[i ^ 1]; for (auto x : cur) { LL l = x; LL r = x + t * (i == 1 ? 1 : -1) * 2ll; if (l > r) swap(l, r); auto ll = lower_bound(nxt.begin(), nxt.end(), l); auto rr = upper_bound(nxt.begin(), nxt.end(), r); ans += rr - ll; } } cout << ans << '\n'; return 0; }
E - Random Swaps of Balls (abc360 E)
题目大意
个球,其中第一个是黑球,其余是白球。
进行以下操作 次:
- 随机两次独立选取 ,交换第 个球和第 个球。
问黑球位置的期望值。
解题思路
假设最终黑球位于第个球的概率是,根据期望定义,答案就是 。考虑如何求。
容易发现第 个球没有本质区别,其概率都是一样的,因此最终我们需要求的就两个数
- 即位于第一个球的概率
- 即位于非第一个球的概率
容易发现经过第次操作的概率,仅依赖于第 次的情况,因此可以迭代球,即设 表示经过 次操作后,球位于第一个球/不位于第一个球的概率。
转移就考虑本次操作是否将球移动到第一个球或非第一个球。设移动概率,不移动概率,移动到某一个位置的概率为
,不动或者从非一球移动到一球。
,从一球移动或者从非一球移动到非移动(全概率-移动到一球的概率)
最后,答案就是。
时间复杂度为。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int mo = 998244353; long long qpower(long long a, long long b) { long long qwq = 1; while (b) { if (b & 1) qwq = qwq * a % mo; a = a * a % mo; b >>= 1; } return qwq; } long long inv(long long x) { return qpower(x, mo - 2); } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, k; cin >> n >> k; int invn = inv(n); int invn1 = inv(n - 1); int move = 2ll * invn * (n - 1) % mo * invn % mo; int stay = (1ll - move + mo) % mo; int move1 = 1ll * move * invn1 % mo; array<int, 2> dp{}; dp[0] = 1; for (int i = 0; i < k; i++) { array<int, 2> dp2{}; dp2[0] = (1ll * dp[0] * stay % mo + 1ll * dp[1] * move1 % mo) % mo; dp2[1] = (1ll * dp[0] * move % mo + 1ll * dp[1] * ((1 - move1 + mo) % mo) % mo) % mo; dp.swap(dp2); } int ans = (dp[0] + 1ll * (n + 2) * (n - 1) / 2 % mo * dp[1] % mo * invn1 % mo) % mo; cout << ans << '\n'; return 0; }
F - InterSections (abc360 F)
题目大意
给定个区间,若俩区间有交叉 ,则有。
设 表示与 有交叉的区间数。
求最大值。
解题思路
<++>
神奇的代码
G - Suitable Edit for LIS (abc360 G)
题目大意
给定一个数组,进行一次操作,使得最长上升子序列的长度最大。
操作为,将任意一个数改为任意一个数。
解题思路
这种有一次修改的,可以从修改处考虑,其修改处左右两边是一个正常的最长上升子序列问题。
因此事先求出表示从左到右,选了第 个数字的最长上升子序列长度,表示从右到左,选了第 个数字的最长下降子序列长度,需要使用的方法。考虑如何将 和 组合。
注意到是严格上升,如果枚举 ,思考 有什么条件,以及 如何找。
枚举 ,考虑 ,则有 需要修改,因此 ,同时由于严格递增, ,这样才能更改成对应的数,使得 和 拼接起来得到一个更长的最长上升子序列。
即需要解决这么一个问题。容易发现这就是一个朴素的二维偏序问题,用线段树求解即可。注意需要对离散化。
的一种最长上升子序列的求法,即表示最长上升子序列长度为 的末尾数字(最大数字)的最小值。注意到 是一个递增的数组,因此对于当前数字 ,可以二分找到它可以接在哪个数字的后面,进而知道了当前的,然后更新 数组。或者朴素的 的 ,分析转移式可以注意到也是一个二维偏序问题,也可以用线段树解决。
用线段树解二维偏序,下标是,值是。即当前求出来后,将 插入到线段树里,即将 位置的值赋予 。
这样在求时,线段树里的值都是 的 的值,自然满足第一个 的条件,因为线段树的下标是 ,而 相当于一个区间 ,要求的就是这个区间的最大值,因为线段树维护的就是,故区间查询最大值、单点修改即可。而由于很大,为作为线段树的下标,需要离散化。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int inf = 1e9 + 8; const int N = 2e5 + 8; class segment { #define lson (root << 1) #define rson (root << 1 | 1) public: int maxx[N << 2]; void build(int root, int l, int r) { if (l == r) { maxx[root] = 0; return; } int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r); maxx[root] = max(maxx[lson], maxx[rson]); } void update(int root, int l, int r, int pos, int val) { if (l == r) { maxx[root] = max(maxx[root], val); return; } int mid = (l + r) >> 1; if (pos <= mid) update(lson, l, mid, pos, val); else update(rson, mid + 1, r, pos, val); maxx[root] = max(maxx[lson], maxx[rson]); } LL query(int root, int l, int r, int L, int R) { if (L <= l && r <= R) { return maxx[root]; } int mid = (l + r) >> 1; int resl = -inf, resr = -inf; if (L <= mid) resl = query(lson, l, mid, L, R); if (R > mid) resr = query(rson, mid + 1, r, L, R); return max(resl, resr); } } sg; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n); for (auto& x : a) cin >> x; auto solve = [&](vector<int>& a) -> vector<int> { vector<int> dp(n, 1); vector<int> pos(n + 1, inf); pos[0] = -inf; for (int i = 0; i < n; i++) { auto it = lower_bound(pos.begin(), pos.end(), a[i]) - pos.begin(); dp[i] = it; pos[it] = min(pos[it], a[i]); } return dp; }; auto pre = solve(a); reverse(a.begin(), a.end()); transform(a.begin(), a.end(), a.begin(), [](int x) { return -x; }); auto suf = solve(a); reverse(suf.begin(), suf.end()); reverse(a.begin(), a.end()); transform(a.begin(), a.end(), a.begin(), [](int x) { return -x; }); vector<int> b(a.begin(), a.end()); b.push_back(-inf); sort(b.begin(), b.end()); b.erase(unique(b.begin(), b.end()), b.end()); auto rank = [&](int x) { return lower_bound(b.begin(), b.end(), x) - b.begin() + 1; }; int ans = 0; int m = b.size(); sg.build(1, 1, m); for (int i = 0; i < n; ++i) { int cnt = sg.query(1, 1, m, 1, rank(a[i] - 1) - 1); ans = max(ans, suf[i] + cnt + (i != 0)); if (i > 0) sg.update(1, 1, m, rank(a[i - 1]), pre[i - 1]); } ans = max(ans, sg.query(1, 1, m, 1, m) + 1); cout << ans << '\n'; return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/18277192
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步