ABC 448 个人题解
ABC448 个人题解
其实我觉得这一场比赛应该要有题目是一些有关 448 这个数的性质的……
A. chmin
题意
给你长度为 \(N\) 的数组 \(A\) 和一个整数 \(X\), 对于 \(i\) 从 \(1\) 到 \(N\), 判断 \(A_i\) 是否小于 \(X\), 小于输出 \(1\) 并把 \(X\) 赋为 \(A_i\), 否则输出 \(0\)
思路
直接模拟, 这里没有读完 \(A\) 数组, 时间/空间 \(O(n)/O(1)\)
代码
// A - chmin
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_a
// Made by Billlly 喵~
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;
int tests = 1;
void solve() {
int n, x;
std::cin >> n >> x;
for (int i = 1; i <= n; ++i) {
int a;
std::cin >> a;
if (a < x) {
x = a;
std::cout << 1 << '\n';
} else
std::cout << 0 << '\n';
}
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
while (tests--) {
solve();
}
return 0;
}
B. Pepper Addiction
题意
有 \(N\) 盘菜和 \(M\) 种胡椒
第 \(i\) 盘菜有两个属性 \(A_i\) 和 \(B_i\), 代表这盘菜最多放 \(B_i \ g\) 胡椒 \(A_i\)
第 \(i\) 种胡椒有一个属性 \(C_i\), 表示最多使用 \(C_i \ g\) 该种胡椒
现在问你最多可以使用多少胡椒
思路
对于每盘菜, 因为最多放 \(B_i\), 所以我们就尽量放满, 后面没有了所需要的胡椒就不放了, 显然是正确的
时间/空间 \(O(n)/O(n + m)\)
我写的时候排序了, 实际上是 useless 的
代码
// B - Pepper Addiction
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_b
// Made by Billlly 喵~
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;
int tests = 1;
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> c(m);
for (int i = 0; i < m; ++i)
std::cin >> c[i];
std::vector<std::pair<int, int>> dish(n);
for (int i = 0; i < n; ++i) {
std::cin >> dish[i].first >> dish[i].second;
--dish[i].first;
}
i64 ans = 0;
for (int i = 0; i < n; ++i) {
int tp = dish[i].first;
int amt = dish[i].second;
int use = std::min(c[tp], amt);
ans += use;
c[tp] -= use;
}
std::cout << ans << '\n';
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
while (tests--) {
solve();
}
return 0;
}
C. Except and Min
题意
给定长度为 \(N\) 的数组 \(A\), 另有 \(Q\) 次查询
每次查询给定 \(K\) 个数 \(B_1 ... B_K\), 然后问你如果在 \(A\) 中删除这若干个数(各一个), 最小值是多少
思路
注意到 \(K\) 只有 \(5\), 所以我们直接 multiset 做完了, 时间复杂度 \(O(QK log N)\)
代码
// C - Except and Min
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_c
// Made by Billlly 喵~
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;
int tests = 1;
void solve() {
int n, q;
std::cin >> n >> q;
std::vector<int> a(n + 1);
std::multiset<int> st;
for (int i = 1; i <= n; ++i) {
std::cin >> a[i];
st.emplace(a[i]);
}
while (q--) {
int k;
std::cin >> k;
std::vector<int> tmp;
for (int i = 1; i <= k; ++i) {
int b;
std::cin >> b;
tmp.emplace_back(a[b]);
st.erase(st.find(a[b]));
}
std::cout << *st.begin() << '\n';
for (const auto& x : tmp)
st.emplace(x);
}
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
while (tests--) {
solve();
}
return 0;
}
D. Integer-duplicated Path
题意
给定一颗 \(N\) 个节点的点权树, 问你对于所有的 \(i\) 从 \(1 \sim N\), 路径 \(1 - i\) 是否存在两个相同权值的节点
思路
DFS + 桶, 从节点 \(1\) 开始遍历整棵树, 时间复杂度 \(O(n)\), 细节见代码
代码
// D - Integer-duplicated Path
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_d
// Made by Billlly 喵~
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;
int tests = 1;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++i)
std::cin >> a[i];
std::vector<std::vector<int>> adj(n + 1);
for (int i = 1; i < n; ++i) {
int u, v;
std::cin >> u >> v;
adj[u].emplace_back(v);
adj[v].emplace_back(u);
}
std::vector<std::string> ans(n + 1);
std::unordered_map<int, int> cnt;
int dup = 0;
auto dfs = [&](auto&& dfs, int u, int p) -> void {
cnt[a[u]] += 1;
if (cnt[a[u]] == 2)
dup += 1;
ans[u] = (dup > 0) ? "Yes" : "No";
for (const auto& v : adj[u]) {
if (v == p)
continue;
dfs(dfs, v, u);
}
cnt[a[u]] -= 1;
if (cnt[a[u]] == 1)
dup -= 1;
};
dfs(dfs, 1, 0);
for (int i = 1; i <= n; ++i)
std::cout << ans[i] << '\n';
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
while (tests--) {
solve();
}
return 0;
}
E. Simple Division
题意
给定 \(M\) 和一个大数 \(N\) 的游程编码, 求 \(\lfloor \frac{N}{M} \rfloor \bmod 10007\)
游程编码是说给定 \(K\) 个二元组 \((c_1, l_1) \dots (c_K, l_K)\), 数 \(N\) 由 \(l_1\) 个 \(c_1\) 连接 \(l_2\) 个 \(c_2\) 一直连接到 \(l_K\) 个 \(c_K\)
思路
直接构造 \(N\) 显然不可行, 注意到 \(N\) 最大可能有 \(10^{14}\) 位, 无法直接表示
考虑模运算的性质, 设 \(x = N \bmod 10007\), \(y = N \bmod M\)
则有:
于是:
其中除法是模 \(10007\) 意义下的乘法逆元, 即乘以 \(M\) 在模 \(10007\) 下的逆元
现在问题转化为分别计算 \(x = N \bmod 10007\) 和 \(y = N \bmod M\)
考虑从高位到低位构造 \(N\) 的模 \(P\) 值 (\(P = 10007\) 或 \(P = M\))
- 当前值 \(cur\), 新增 \(l\) 个数字 \(d\)
- 新的值 \(cur' = cur \times 10^l + d \times (10^{l-1} + 10^{l-2} + \dots + 1) \pmod{P}\)
因此我们需要快速计算 \(10^l \bmod P\) 和等比数列和 \(S = \frac{10^l - 1}{9} \bmod P\)
由于 \(P\) 可能是任意数, 需要处理分母 \(9\) 的逆元存在性问题
但我们实际上可以直接计算 \(d \times S \bmod P\), 而 \(S\) 可以用递推公式计算:
这样 \(S_l\) 即为所求, 时间复杂度 \(O(l)\), 但是 \(l\) 可达 \(10^9\) 就爆炸了
于是不难想到用快速幂优化, 设 \(f(a, b) = (a^b, 1 + a + a^2 + \dots + a^{b-1}) \pmod{P}\)
则有递推关系:
- 若 \(b\) 为偶数: \(f(a, b) = f(a, b/2)^2\)
- 若 \(b\) 为奇数: \(f(a, b) = f(a, b-1) \times a\)
具体实现时用 pair 存储 \((a^b, 1+a+\dots+a^{b-1})\)
总时间复杂度 \(O(K \log \max(l_i))\), 满足限制
代码
// E - Simple Division
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_e
// Made by Billlly 喵~
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;
int tests = 1;
void solve() {
i64 K, M;
std::cin >> K >> M;
const i64 MOD = 10007;
auto pm = i64 a, i64 b, i64 mod {
i64 res = 1 % mod;
while (b > 0) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
};
auto ps = i64 a, i64 n, i64 mod {
if (n == 0) return std::pair{1 % mod, 0LL};
std::pair<i64, i64> res = {1 % mod, 0};
i64 base_a = a;
i64 base_s = 1;
while (n > 0) {
if (n & 1) {
res.first = res.first * base_a % mod;
res.second = (res.second * base_a + base_s) % mod;
}
base_s = base_s * (1 + base_a) % mod;
base_a = base_a * base_a % mod;
n >>= 1;
}
return res;
};
i64 invM = pm(M, MOD - 2, MOD);
i64 nm = 0, r = 0;
for (i64 i = 0; i < K; ++i) {
char c;
i64 l;
std::cin >> c >> l;
i64 d = c - '0';
auto t1 = ps(10, l, MOD);
nm = (nm * t1.first + d * t1.second) % MOD;
auto t2 = ps(10, l, M);
r = (r * t2.first + d * t2.second) % M;
}
i64 ans = (nm - r) % MOD;
if (ans < 0) ans += MOD;
ans = ans * invM % MOD;
std::cout << ans << '\n';
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
while (tests--) {
solve();
}
return 0;
}
F. Authentic Traveling Salesman Problem
题意
给定 \(N\) 个平面点 \((x_i, y_i)\), 找一条从 \(1\) 出发遍历每个其它点恰好一次的回路, 满足相邻两个遍历的节点的曼哈顿距离之和不超过 \(10 ^ {10}\)
思路
其实这很像莫队的优化, 具体可以看看这一道题目
然后就做完了
代码
// F - Authentic Traveling Salesman Problem
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_f
// Made by Billlly 喵~
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;
int tests = 1;
void solve() {
int n;
std::cin >> n;
struct pt {
int x, y, id;
};
std::vector<pt> v(n);
for (int i = 0; i < n; ++i) {
std::cin >> v[i].x >> v[i].y;
v[i].id = i + 1;
}
if (n == 1) {
std::cout << 1 << '\n';
return;
}
std::sort(v.begin(), v.end(), [](pt a, pt b) { return a.x < b.x; });
static const int blk = std::max((int)std::sqrt(n), 1);
for (int i = 0; i < n; i += blk) {
int r = std::min(i + blk, n);
int b = i / blk;
auto cmp = [b](pt p, pt q) {
if (b % 2 == 0) {
return p.y < q.y;
} else {
return p.y > q.y;
}
};
std::sort(v.begin() + i, v.begin() + r, cmp);
}
int bg = 0;
for (int i = 0; i < n; ++i) {
if (v[i].id == 1) {
bg = i;
break;
}
}
for (int i = 0; i < n; ++i) {
int pos = (bg + i) % n;
std::cout << v[pos].id << " \n"[i == n - 1];
}
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
while (tests--) {
solve();
}
return 0;
}
G. Conquest
感觉是不可做的 AI 题, 润

浙公网安备 33010602011771号