AtCoder Beginner Contest 344 A-G 题解
A - Spoiler
Question
删除两个 | 之间的字符
Solution
按照题意模拟即可
Code
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
string p1, p2;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '|') break;
p1.push_back(s[i]);
}
for (int i = s.size() - 1; i >= 0; i--) {
if (s[i] == '|') break;
p2.push_back(s[i]);
}
reverse(p2.begin(), p2.end());
cout << p1 + p2 << endl;
return 0;
}
B - Delimiter
Question
倒叙输出输入的数
Code
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> p;
int x;
while (cin >> x) p.push_back(x);
reverse(p.begin(), p.end());
for (auto &x : p) cout << x << '\n';
return 0;
}
C - A+B+C
Question
给出三个集合
Solution
由于集合大小很小,
Code
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m, l;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
cin >> m;
vector<int> b(m);
for (int i = 0; i < m; i++) cin >> b[i];
cin >> l;
vector<int> c(l);
for (int i = 0; i < l; i++) cin >> c[i];
unordered_map<int, int> mp;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
for (int k = 0; k < l; k++)
mp[a[i] + b[j] + c[k]] = 1;
int q; cin >> q;
while (q--) {
int x; cin >> x;
cout << (mp.count(x) ? "Yes" : "No") << endl;
}
return 0;
}
D - String Bags
Question
初始时有一个空字符串
袋子
- 支付
元,从袋子中选择一个字符串,将其连接到 的末尾 - 什么也不做
给定一个字符串
Solution
一眼背包问题,定义
对于每个袋子中的一个串
Code
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int main() {
string T; cin >> T; T = " " + T;
int n; cin >> n;
vector<vector<string> > a(n + 1);
for (int i = 1; i <= n; i++) {
int m; cin >> m;
for (int j = 0; j < m; j++) {
string s; cin >> s;
a[i].push_back(s);
}
}
vector<vector<int> > dp(n + 1, vector<int>(T.size(), INF));
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j < T.size(); j++) {
for (int k = 0; k < a[i].size(); k++) {
if (j + a[i][k].size() - 1 >= T.size()) continue;
if (T.substr(j, a[i][k].size()) == a[i][k])
dp[i][j + a[i][k].size() - 1] = min(dp[i][j + a[i][k].size() - 1], dp[i - 1][j - 1] + 1);
}
}
for (int j = 0; j < T.size(); j++)
dp[i][j] = min(dp[i][j], dp[i - 1][j]);
}
int ans = INF;
for (int i = 1; i <= n; i++)
ans = min(ans, dp[i][T.size() - 1]);
cout << (ans == INF ? -1 : ans) << endl;
return 0;
}
E - Insert or Erase
Question
给定长度为
按照给定序列的顺序处理
1 x y:在中元素 之后插入 2 x:从中移除元素
输出处理完所有询问后的
Solution
这是一个非常好的题,如果
但是本题的
而 C++ 的 map 就很好的做到了这一点,使用 map 看成数组记录链表的
Code
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int main() {
ios::sync_with_stdio(false);
int n; cin >> n;
unordered_map<int,int> right, left;
vector<int> a(n + 1, 0);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++) {
if (i != 1) left[a[i]] = a[i - 1];
if (i != n) right[a[i]] = a[i + 1];
}
left[a[1]] = -INF; right[a[n]] = INF;
left[INF] = a[n]; right[-INF] = a[1];
int q; cin >> q;
while (q--) {
int opt; cin >> opt;
if (opt == 1) {
int x, y; cin >> x >> y;
const int L = x, R = right[x];
left[y] = L; right[y] = R;
right[L] = y; left[R] = y;
}
if (opt == 2) {
int x; cin >> x;
const int L = left[x], R = right[x];
right[L] = R; left[R] = L;
left.erase(x); right.erase(x);
}
}
int x = right[-INF];
while (x != INF) {
cout << x << " ";
x = right[x];
}
return 0;
}
F - Earn to Advance
Question
有一个
当高桥处于
- 留在原地并增加金钱
- 支付
移动到方格 - 支付
移动到方格
问高桥移动到
Solution
高桥移动的过程肯定是移动到一个点,然后攒够足够的钱,然后尽量把一个现有的钱用完去移动到下一个点,然后再在下一个点攒钱
因为如果有两个点
所以这样就可以定义
我们只需要枚举上一次攒钱的地方
那么现在这个状态的步数和剩下的钱也就迎刃而解了
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pll;
const LL INF = 0x3f3f3f3f3f3f3f3f;
int main() {
int n; cin >> n;
vector<vector<LL> > P(n + 1, vector<LL>(n + 1)), D(n + 1, vector<LL>(n + 1)), R(n + 1, vector<LL>(n + 1));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> P[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j < n; j++)
cin >> R[i][j];
for (int i = 1; i < n; i++)
for (int j = 1; j <= n; j++)
cin >> D[i][j];
auto get_dis = [&](int x, int y) {
vector<vector<LL> > dis(n + 1, vector<LL>(n + 1, INF));
dis[x][y] = 0;
for (int px = n; px >= 1; px--)
for (int py = n; py >= 1; py--) {
if (px < x)
dis[px][py] = min(dis[px][py], dis[px + 1][py] + D[px][py]);
if (py < y)
dis[px][py] = min(dis[px][py], dis[px][py + 1] + R[px][py]);
}
return dis;
};
vector<vector<pll> > dp(n + 1, vector<pll>(n + 1, {INF,INF}));
dp[1][1] = {0,0};
for (int x = 1; x <= n; x++)
for (int y = 1; y <= n; y++) {
auto dis = get_dis(x,y);
for (int px = 1; px <= x; px++)
for (int py = 1; py <= y; py++){
auto [p_step, p_money] = dp[px][py];
LL ex_step = (dis[px][py] - p_money + P[px][py] - 1) / P[px][py]; ex_step = max(ex_step, 0LL);
LL now_step = p_step + ex_step + (x - px) + (y - py);
LL now_money = p_money + ex_step * P[px][py] - dis[px][py];
if (now_step < dp[x][y].first || (now_step == dp[x][y].first && now_money > dp[x][y].second))
dp[x][y] = {now_step, now_money};
}
}
cout << dp[n][n].first << endl;
return 0;
}
G - Points and Comparison
Question
在
给出了
定义
求
Solution
不妨设
观察
由于
所以关键在于如何维护
先考虑极端情况 pair<int,int> 正好满足
考虑
- 如果
,则排序还满足 ,也就是说, 的位置不变 - 如果
也就是说, 两点组成的斜率大于了 ,则交换
考虑到没两个点至多只需要交换一次,所以把询问按照
对于相邻的点
如果说对于某一个
当满足了对于一个
对于时间复杂度分析,最多存在
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pll;
const LL mod = (1ll << 31) - 1;
struct Line {
LL up, dn;
int pre, nxt; // 两者的编号
Line(LL _up, LL _dn, int _pre, int _nxt) {
if (dn < 0) up = -up, dn = -dn;
up = _up, dn = _dn, pre = _pre, nxt = _nxt;
}
bool operator < (const Line &rhs) const {
return up * rhs.dn > dn * rhs.up;
}
bool operator == (const Line &rhs) const {
return up == rhs.up && dn == rhs.dn && pre == rhs.pre && nxt == rhs.nxt;
}
};
signed main() {
ios::sync_with_stdio(0); cin.tie(0);
int n; cin >> n;
vector<pll> a(n + 1);
for (int i = 1; i <= n; i++) {
auto & [k, b] = a[i];
cin >> k >> b;
}
sort (a.begin() + 1, a.end());
int Q; cin >> Q;
int G0, Ra, Rb; cin >> G0 >> Ra >> Rb;
vector<pll> q(Q + 1); vector<LL> G(3 * Q + 1); G[0] = G0;
for (int i = 1; i <= 3 * Q; i++)
G[i] = (48271 * G[i - 1]) % mod;
for (int i = 1; i <= Q; i++) {
auto &[A, B] = q[i];
A = -Ra + (G[3 * i - 2] % (2 * Ra + 1));
B = -Rb + ((G[3 * i - 1] * mod + G[3 * i]) % (2 * Rb + 1));
}
sort (q.begin() + 1, q.end());
auto make_line = [&] (int i, int j) {
return Line(a[j].second - a[i].second, a[j].first - a[i].first, i, j);
};
priority_queue<Line> s;
for (int i = 1; i < n; i++)
if (a[i].first < a[i + 1].first)
s.push (make_line(i, i + 1));
LL ans = 0;
for (int i = 1; i <= Q; i++) {
auto [k, b] = q[i];
while (s.size() > 0) {
auto it = s.top();
int dn = it.dn, up = it.up, pre = it.pre, nxt = it.nxt;
if (!(make_line(pre, nxt) == it)) {s.pop(); continue;} // 说明这个线段已经被更新过了
if (k * dn < up) break; // 当 k > 两点的斜率时,交换
s.pop(); swap(a[pre], a[nxt]);
if (pre > 1 && a[pre - 1].first < a[pre].first) s.push(make_line(pre - 1, pre));
if (nxt < n && a[nxt].first < a[nxt + 1].first) s.push(make_line(nxt, nxt + 1));
}
int l = 1, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
auto [X, Y] = a[mid];
if ( -k * X + Y >= b) r = mid - 1;
else l = mid + 1;
}
ans += n - r;
}
cout << ans << endl;
return 0;
}