2024-07-06 23:36阅读: 414评论: 0推荐: 2

AtCoder Beginner Contest 361

A - Insert (abc361 A)

题目大意

给定一个数组a和数 k,x,将 x插入第 k个数之后,并输出新数组。

解题思路

vector的直接 insert即可。

神奇的代码
#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, k, x;
cin >> n >> k >> x;
vector<int> a(n);
for (auto& i : a)
cin >> i;
a.insert(a.begin() + k, x);
for (auto i : a)
cout << i << ' ';
cout << '\n';
return 0;
}


B - Intesection of Cuboids (abc361 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 a, b, c, d, e, f;
int g, h, i, j, k, l;
cin >> a >> b >> c >> d >> e >> f;
cin >> g >> h >> i >> j >> k >> l;
auto overlap = [](int l1, int r1, int l2, int r2) {
return max(l1, l2) < min(r1, r2);
};
bool x = overlap(a, d, g, j);
bool y = overlap(b, e, h, k);
bool z = overlap(c, f, i, l);
if (x && y && z) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
return 0;
}


C - Make Them Narrow (abc361 C)

题目大意

给定n个数ai,删去其中的 k个数,使其极差(最大值与最小值的差)最小。

解题思路

ai从小到大排序,我删除其中的 k个数,一定是从最小值和最大值开始删除,不会从中间删(这对极差不会有任何影响)。

决策就是我从最小值开始删多少个数,如果我选择删 x个数,那么从最大值开始要删 kx个数。所有的 x的情况的极差取个最小值即为答案。

x的范围就是 O(n),直接枚举 x即可。

神奇的代码
#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, k;
cin >> n >> k;
vector<int> a(n);
for (auto& x : a)
cin >> x;
sort(a.begin(), a.end());
int ans = 1e9 + 7;
for (int i = 0; i <= k; i++) {
ans = min(ans, a[n - (k - i) - 1] - a[i]);
}
cout << ans << '\n';
return 0;
}


D - Go Stone Puzzle (abc361 D)

题目大意

n+2个格子,其中前 n个格子有石头,石头有黑有白。每次操作。

将相邻两个石头移动到无石头的位置,俩石头相对顺序不变。

给定初始局面和最终局面,问操作次数的最小值。

解题思路

注意到n14,局面数最多只有 C147<1e5。因此直接从初始局面进行 BFS,枚举操作,转移后续状态即可。

枚举操作即,先花 O(n)找到空位,然后花 O(n)枚举要移动的两个石头,移动后得到后继状态。转移复杂度即为O(n)

由于数很小,状态记录可以直接用vector,用map记录抵达状态的操作次数,开销不会很大,不用二进制压缩,也方便写转移。

神奇的代码
#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, t;
cin >> n >> s >> t;
vector<int> st(n + 2, 2), ed(n + 2, 2);
for (int i = 0; i < n; i++) {
st[i] = s[i] == 'B';
ed[i] = t[i] == 'B';
}
map<vector<int>, int> cnt;
queue<vector<int>> q;
q.push(st);
cnt[st] = 0;
while (!q.empty()) {
auto u = q.front();
q.pop();
int d = cnt[u];
if (u == ed) {
break;
}
int empty = find(u.begin(), u.end(), 2) - u.begin();
for (int i = 0; i < n + 1; ++i) {
auto v = u;
if (v[i] != 2 && v[i + 1] != 2) {
swap(v[i], v[empty]);
swap(v[i + 1], v[empty + 1]);
if (!cnt.count(v)) {
cnt[v] = d + 1;
q.push(v);
}
}
}
}
if (!cnt.count(ed)) {
cnt[ed] = -1;
}
cout << cnt[ed] << '\n';
return 0;
}


E - Tree and Hamilton Path 2 (abc361 E)

题目大意

给定一棵树,边有边权。问从一个点出发,访问完所有节点的最小路径长度。

解题思路

首先注意到,从一个点出发st,最终停下来的点一定是叶子ed

其次,考虑每条边访问的次数,会发现只有sted这条链上的边只访问了一次,其他边均访问两次。

因此最终的路径长度即为 2eicost(sted),即所有边权和的两倍减去根到叶子的距离

最小化路径长度,即最大化根到叶子的距离

由于根不是指定的,因此要找的是树上两点的最长距离

这即为树的直径,两次 DFS即可找出。

神奇的代码
#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<array<int, 2>>> edge(n);
LL sum = 0;
for (int i = 0; i < n - 1; ++i) {
int u, v, w;
cin >> u >> v >> w;
--u, --v;
edge[u].push_back({v, w});
edge[v].push_back({u, w});
sum += w;
}
vector<LL> dis(n, 0);
auto dfs = [&](auto&& dfs, int u, int fa) -> void {
for (auto [v, w] : edge[u]) {
if (v == fa)
continue;
dis[v] = dis[u] + w;
dfs(dfs, v, u);
}
};
dfs(dfs, 0, 0);
int l = max_element(dis.begin(), dis.end()) - dis.begin();
dis.assign(n, 0);
dfs(dfs, l, l);
LL max_dis = *max_element(dis.begin(), dis.end());
cout << sum * 2 - max_dis << endl;
return 0;
}


F - x = a^b (abc361 F)

题目大意

给定n,求 x[1,n] ,满足存在a,b(b2),使得 x=ab

n1018

解题思路

由于b2,因此 a的范围就是 [1,n],即a[1,109],直接枚举a,b的话是 O(109log109)

注意到当b3时,a[1,106] ,因此先枚举a[1,106] ,把a106,b2x全部找出来,这里的时间复杂度为 O(106log106),全部存在vector然后排序去重即可得到这部分的x的数量。

然后考虑剩下的 106<a109,b=2x的数量。

一个比较浅显的想法,认为这部分的数量为n106。但容易发现会算重:如果a=c2,其中c106,那么 a2=c4 ,这个数其实是上面算过的(a106,b2部分)。

因此要把重复的部分去掉,考虑怎样的a是重复的,即存在b2,a=cb(c106),容易发现这个条件就是上面枚举计算的条件,即重复的都在vector里出现过。

因此n106再减去vector里出现过的(106,n]的数即可,二分找到对应的下标相减即为重复的数量。

神奇的代码
#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);
LL n;
cin >> n;
int up = 1e6;
LL upp = 1e18;
vector<LL> s{1};
for (int i = 2; i <= up; i++) {
__int128 x = 1ll * i * i;
while (x <= n) {
s.push_back(x);
x *= i;
}
}
sort(s.begin(), s.end());
s.erase(unique(s.begin(), s.end()), s.end());
LL ans = s.size();
int half = sqrt(n);
if (half > up) {
ans += half - up;
auto r = upper_bound(s.begin(), s.end(), half) - s.begin();
auto l = upper_bound(s.begin(), s.end(), up) - s.begin();
ans -= r - l;
}
cout << ans << '\n';
return 0;
}


G - Go Territory (abc361 G)

题目大意

二维平面,有障碍物,可以上下左右走。

问有多少个点,不可以走到(1,1)

解题思路

看的时候发觉很久以前做过的类似的题,基本做法一致。

首先看样例给的图

example

一个朴素的想法就是找到不在原点连通块的点,这些点的个数和就是答案。

如果二维平面很小的话,可以对每个点进行BFS,找到所有的连通块,然后累计非原点连通块的点,其值即为答案。

但是这里的平面大小有 105×105,不能BFS

由于每个连通块都是一个封闭的图形,我们要统计的就是这个图形的点数,类似面积,可以使用扫描线的方法。从下往上扫描每一行的线段,这些线段是由障碍物作分割,然后用并查集维护这些线段所属的连通块。

考虑第i行,第 i行的障碍物把该行分割成了 x条线段,并且已经用并查集维护好了这些线段所属的连通块。然后考虑第 i+1行,第 i+1行的障碍物同样把该行分割成了 y条线段,现在我们就需要将这两行线段合并,得到第 i+1行的每个线段所属的连通块是哪个。

合并即考虑上下两行的两个线段,如果它们是相交的,那么它们应属于同一个连通块,并查集合并起来。这是一个模拟的过程,有点小细节。

得到第 i+1行的线段的连通块关系,继续合并第 i+2行,依次往复,扫描整个平面。最后遍历所有连通块,把不在原点连通块的点累加即为答案。

而对于该行没有障碍物的,则视为一条线段,如果有连续若干行无障碍物,这我们可以把这若干行压成一行,视为一条线段。最后扫描时按照障碍物的第一维排序扫描。

特殊处理无障碍物的情况。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
struct segg {
int l, r, id;
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, h = 2e5 + 8, w = 2e5 + 8; // 大平面[1, h] * [1, w]
cin >> n;
vector<pair<int, int>> pos(n);
for (auto& i : pos) {
cin >> i.first >> i.second;
i.first += 2;
i.second += 2; // 原点视为(1, 1)
}
sort(pos.begin(), pos.end());
int cur = 0;
vector<int> f;
vector<LL> cnt;
array<vector<segg>, 2> seg;
auto findfa = [&](auto& findfa, int x) -> int {
return f[x] == x ? x : f[x] = findfa(findfa, f[x]);
};
auto crossover = [&](segg& a, segg& b) { return a.r >= b.l && a.l <= b.r; };
auto add_seg = [&](int l, int r, LL cc) {
int id = f.size();
f.push_back(id);
cnt.push_back(cc);
seg[cur].push_back({l, r, id});
};
auto unionn = [&]() { // 将上下两行线段合并
auto& last_seg = seg[cur ^ 1];
auto& cur_seg = seg[cur];
auto last_pt = last_seg.begin();
for (auto& i : cur_seg) {
while (true) {
if (last_pt != last_seg.end() &&
crossover(i, *last_pt)) { // 线段相交
int fa = findfa(findfa, i.id);
int fb = findfa(findfa, last_pt->id);
if (fa != fb) {
f[fa] = fb;
cnt[fb] += cnt[fa];
}
last_pt = next(last_pt);
} else if (last_pt == last_seg.end() ||
last_pt->l > i.r) { // 形如 cur_pt ..... last_pt
if (last_pt != last_seg.begin())
last_pt = prev(last_pt);
break;
} else // 形如 last_pt ..... cur_pt
last_pt = next(last_pt);
}
}
};
auto skip_line = [&](int l, int r) { // 空行[l, r],无障碍物
if (l > r)
return;
cur ^= 1;
seg[cur].clear();
add_seg(1, w, (r - l + 1ll) * w);
unionn();
};
auto solve_pos = [&](int l, int r) { // 处理该行的所有障碍物pos[l..r]
if (l > r)
return;
cur ^= 1;
seg[cur].clear();
int la = 0;
for (int i = l; i <= r; ++i) {
if (pos[i].second - la > 1) {
add_seg(la + 1, pos[i].second - 1, pos[i].second - la - 1);
}
la = pos[i].second;
}
if (w > la) {
add_seg(la + 1, w, w - la);
}
unionn();
};
LL ans = 0;
if (n == 0) {
ans = 0;
} else {
skip_line(1, pos[0].first - 1);
int la = 0;
for (int i = 1; i < n; ++i) {
if (pos[i].first != pos[la].first) {
solve_pos(la, i - 1); // 处理同行的所有障碍物
skip_line(pos[la].first + 1, pos[i].first - 1); // 处理空行
la = i;
}
}
solve_pos(la, n - 1);
skip_line(pos[la].first + 1, h); // 最顶部还有空行
int origin = findfa(findfa, seg[cur].front().id); // 起点的连通块编号
for (int i = 0; i < f.size(); ++i) {
if (findfa(findfa, i) == i && i != origin) {
ans += cnt[i];
}
}
}
cout << ans << '\n';
return 0;
}


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/18288078

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(414)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.