Codeforces Round 1037 (Div. 3)
A. Only One Digit
题意:求和\(x\)有至少一个相同数字且最小的数。
取\(x\)数位中最小的数。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int x;
std::cin >> x;
int ans = 9;
while (x) {
ans = std::min(ans, x % 10);
x /= 10;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
B. No Casino in the Mountains
题意:一个\(01\)数组,所有连续\(0\)子数组拿\(k\)个空一个,如此循环直到拿不了。求总共放几个长度为\(k\)的子数组。
模拟。
对于一个长度为\(len\)的全\(0\)子数组,贡献为\(\lfloor \frac{len}{k+1} \rfloor + [len \% (k + 1) = k]\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int ans = 0;
for (int i = 0; i < n; ++ i) {
if (a[i] == 0) {
int j = i;
while (j + 1 < n && a[j + 1] == 0) {
++ j;
}
int len = j - i + 1;
ans += len / (k + 1) + (len % (k + 1) == k);
i = j;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
C. I Will Definitely Make It
题意:一些高度为\(h_i\)的柱子。一开始在第\(k\)个柱子上。从一个柱子到另一个柱子花费时间是高度差,且到达之前高度都不变。一开始水位高度为\(1\)。每秒上升\(1\)。求能不能到最高的位置。
只和高度有关,直接排序。然后相邻两个爬就行了。因为发现\(r - l = r - k + k - l\)。所以一直跳距离最近的位置是最优的,模拟判断就行了。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
-- k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int h = a[k];
std::ranges::sort(a);
for (int i = 0, j = 0; i < n; ++ i) {
if (a[i] <= h) {
continue;
}
if (a[i] - h + j > h) {
std::cout << "NO\n";
return;
}
j += a[i] - h;
h = a[i];
}
std::cout << "YES\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
D. This Is the Last Time
题意:一开始有\(k\)元,有\(n\)个赌场,如果\(l_i \leq k \leq r_i\)就可以把\(k\)变成\(w_i\), \(w_i \in [l_i, r_i]\)。求最多有多少钱。
不可能有一步减少钱的情况。因为如果需要变成一个\(k' < k\),如果有\(l_i \leq k' \leq r_i\)且\(w_i > k\),那么\(l_i \leq k \leq r_i\)。因为\(k < w_i \leq r_i\)。所以按\(w_i\)排序,循环跑一遍。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<std::tuple<int, int, int>> a(n);
for (int i = 0; i < n; ++ i) {
int l, r, w;
std::cin >> l >> r >> w;
a[i] = {w, l, r};
}
std::ranges::sort(a);
for (auto & [w, l, r] : a) {
if (l <= k && k <= r) {
k = std::max(k, w);
}
}
std::cout << k << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
E. G-C-D, Unlucky!
题意:\(p\)是\(a\)的前缀\(gcd\)数组,\(s\)是\(a\)的后缀\(gcd\)数组。求有没有一个合法的\(a\)。
直接猜的。
就是判断是不是所有的\(i\)都有\(gcd(p_i, s_{i+1}) = s_1\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n), b(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < n; ++ i) {
std::cin >> b[i];
}
for (int i = 0; i < n; ++ i) {
if (i + 1 < n && a[i] % a[i + 1]) {
std::cout << "NO\n";
return;
}
if (i && b[i] % b[i - 1]) {
std::cout << "NO\n";
return;
}
}
if (b[0] != a.back()) {
std::cout << "NO\n";
return;
}
for (int i = 0; i + 1 < n; ++ i) {
if (std::gcd(a[i], b[i + 1]) != b[0]) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
F. 1-1-1, Free Tree!
题意:一棵树,每个点有颜色,每条边有权值。如果一条边两个点颜色相同,代价为\(0\),否则代价为权值。每次修改一个点的颜色,求总代价。
upd:根号分治被hack了。
正确做法应该是\(dfs\)一遍树,然后每个点用\(map\)存子节点不同颜色的边权和。
每次改变一个点就和父节点单独讨论一下,改一下父节点的\(map\),然后跟子节点讨论直接用\(map\)加减。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, q;
std::cin >> n >> q;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<std::pair<int, int>>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v, w;
std::cin >> u >> v >> w;
-- u, -- v;
adj[u].emplace_back(v, w);
adj[v].emplace_back(u, w);
}
std::vector<std::map<int, i64>> mp(n);
i64 ans = 0;
std::vector<int> fa(n, -1), val(n);
auto dfs = [&](auto & self, int u) -> void {
for (auto & [v, w] : adj[u]) {
if (v == fa[u]) {
continue;
}
fa[v] = u;
val[v] = w;
mp[u][a[v]] += w;
self(self, v);
if (a[u] != a[v]) {
ans += w;
}
}
};
dfs(dfs, 0);
while (q -- ) {
int u, x;
std::cin >> u >> x;
-- u;
if (fa[u] != -1) {
if (a[u] == a[fa[u]]) {
ans += val[u];
}
if (x == a[fa[u]]) {
ans -= val[u];
}
mp[fa[u]][a[u]] -= val[u];
mp[fa[u]][x] += val[u];
}
if (mp[u].count(a[u])) {
ans += mp[u][a[u]];
}
if (mp[u].count(x)) {
ans -= mp[u][x];
}
a[u] = x;
std::cout << ans << "\n";
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
G. Big Wins!
题意:求一个数组的一个子区间的中位数减最小值的最大值。
一个\(trick\)是,如果我们求\(med\)是不是大于等于\(m\),可以把数组里小于\(m\)的位置设为\(-1\),否则设为\(1\)。那么一个子区间\(med\)大于等于\(m\),则区间和大于等于\(0\)。
那么我们可以用维护区间最大最小值的线段树维护前缀和的最值,枚举中位数,每次操作完后把等于当前枚举的中位数的位置的后缀都减去\(-2\),因为是从\(1\)变为了\(-1\),减少了\(2\),然后因为是维护的前缀和,所以对后面的位置都要减\(2\)。
那么固定了中位数,怎么求最小值?我们可以把\((a_i, i)\)排序,那么就可以从小的数遍历大的数,我们用一个单调栈预处理每个位置作为最小值的左右区间\([L_i, R_i]\),如果\([i, R_i]\)的最大值减\([L_i - 1, i - 1]\)的最小值小于\(0\)则以这个位置为最小值不行。因为中位数也是从小到大枚举,那么当前位置对于这个中位数不行,对于更大的中位数也不行,则可以用一个指针维护,做到线性枚举。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
template <class Info, class Tag>
struct LazySegmentTree {
struct Node {
int l, r;
Info info;
Tag tag;
};
std::vector<Node> tr;
LazySegmentTree() {}
LazySegmentTree(int n) {
init(n);
}
LazySegmentTree(const std::vector<int> & a) {
init(a);
}
void init(int n) {
tr.assign(n << 2, {});
build(0, n - 1);
}
void init(const std::vector<int> & a) {
int n = a.size();
tr.assign(n << 2, {});
auto build = [&](auto & self, int l, int r, int u = 1) -> void {
tr[u] = {l, r, {}};
if (l == r) {
tr[u].info.max = tr[u].info.min = a[l];
return;
}
int mid = l + r >> 1;
self(self, l, mid, u << 1); self(self, mid + 1, r, u << 1 | 1);
pushup(u);
};
build(build, 0, n - 1);
}
void pushdown(Node & u, const Tag & tag) {
u.info = u.info + tag;
u.tag = u.tag + tag;
}
void pushdown(int u) {
if (tr[u].tag.exist()) {
pushdown(tr[u << 1], tr[u].tag);
pushdown(tr[u << 1 | 1], tr[u].tag);
tr[u].tag.clear();
}
}
void pushup(int u) {
tr[u].info = tr[u << 1].info + tr[u << 1 | 1].info;
}
void build(int l, int r, int u = 1) {
tr[u] = {l, r, {}};
if (l == r) {
return;
}
int mid = l + r >> 1;
build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
pushup(u);
}
void modify(int l, int r, const Tag & tag, int u = 1) {
if (l <= tr[u].l && tr[u].r <= r) {
pushdown(tr[u], tag);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) {
modify(l, r, tag, u << 1);
}
if (r > mid) {
modify(l, r, tag, u << 1 | 1);
}
pushup(u);
}
Info query(int l, int r, int u = 1) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].info;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) {
return query(l, r, u << 1);
} else if (l > mid) {
return query(l, r, u << 1 | 1);
}
return query(l, r, u << 1) + query(l, r, u << 1 | 1);
}
};
struct Info {
int max, min;
};
struct Tag {
int add;
bool exist() {
return add != 0;
}
void clear() {
add = 0;
}
};
Info operator + (const Info & a, const Info & b) {
Info res{};
res.max = std::max(a.max, b.max);
res.min = std::min(a.min, b.min);
return res;
}
Info operator + (const Info & a, const Tag & b) {
Info res{};
res.max = a.max + b.add;
res.min = a.min + b.add;
return res;
}
Tag operator + (const Tag & a, const Tag & b) {
Tag res{};
res.add = a.add + b.add;
return res;
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
std::vector<std::pair<int, int>> b;
std::vector<std::vector<int>> pos(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
pos[a[i]].push_back(i);
b.emplace_back(a[i], i);
}
a.push_back(0);
std::ranges::sort(b);
std::stack<int> stk;
stk.push(0);
std::vector<int> L(n + 2), R(n + 2);
for (int i = 1; i <= n + 1; ++ i) {
while (a[stk.top()] > a[i]) {
R[stk.top()] = i - 1;
stk.pop();
}
L[i] = stk.top() + 1;
stk.push(i);
}
std::vector<int> info(n + 1, 1);
info[0] = 0;
for (int i = 1; i <= n; ++ i) {
info[i] += info[i - 1];
}
LazySegmentTree<Info, Tag> tr(info);
auto check = [&](int p) -> bool {
int max = tr.query(p, R[p]).max;
int min = tr.query(L[p] - 1, p - 1).min;
return max - min >= 0;
};
int ans = 0;
for (int med = 1, p = 0; med <= n; ++ med) {
while (p < n && !check(b[p].second)) {
++ p;
}
if (p < n) {
ans = std::max(ans, med - a[b[p].second]);
}
for (auto & i : pos[med]) {
tr.modify(i, n, Tag{-2});
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}