Codeforces 杂题集(其三)
记录一些没有写在其他随笔中的 Codeforces 杂题, 以 Problemset 题号排序
1427C - The Hard Work of Paparazzi(2000)
从网格起点 \((1,1)\) 出发,已知有 \(n\) 个目标分别在严格递增的指定时间 \(t_i\) 瞬间出现在坐标 \((x_i, y_i)\),在移动耗时等于两点间曼哈顿距离的条件下,求最多能准时赶到并访问多少个目标。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int r, n;
cin >> r >> n;
vector<array<int, 3>> a(n + 1);
a[0] = {0, 1, 1};
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 3; j++) cin >> a[i][j];
}
vector<int> dp(n + 1, INT_MIN);
dp[0] = 0;
int pre_max = INT_MIN;
for (int i = 1; i <= n; i++) {
for (int j = max(i - 2 * r, 0); j < i; j++) {
if (abs(a[i][1] - a[j][1]) + abs(a[i][2] - a[j][2]) <=
a[i][0] - a[j][0]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
dp[i] = max(dp[i], pre_max + 1);
if (i - 2 * r >= 0) pre_max = max(pre_max, dp[i - 2 * r]);
}
cout << *max_element(dp.begin(), dp.end()) << '\n';
}
1207F - Remainder Problem(2100)
维护一个初始全0的数组,支持单点加值(将 \(a_x\) 增加 \(y\)),以及查询所有下标模 \(x\) 等于 \(y\) 的元素之和。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e5;
const int SN = sqrt(N);
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<int> a(N + 1);
vector b(SN + 1, vector<int>(SN + 1));
int q;
cin >> q;
while (q--) {
int t, x, y;
cin >> t >> x >> y;
if (t == 1) {
a[x] += y;
for (int i = 1; i <= SN; i++) {
b[i][x % i] += y;
}
} else {
if (x <= SN) cout << b[x][y] << '\n';
else {
int ans = 0;
for (int i = y; i <= N; i += x) {
ans += a[i];
}
cout << ans << '\n';
}
}
}
}
1187C - Vasya And Array(1800)
已知一个未知数组的若干个子区间是否单调不降的限制条件,要求构造出一个满足所有限制的数组,或判断无解。
题解
不妨让整个数组是 \(n, n-1, n-2, ..., 1\),这样整个数组都是逆序。如果有查询确定一段区间是单调不降的,就让这个区间所有值都变成开头的那个数,这样就尽可能地不破坏大部分逆序。查询区间逆序时只要确认首位是否相等即可,因为整个数组都是单调不升的。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<array<int, 3>> v(m);
for (int i = 0; i < m; i++) {
for (int j = 0; j < 3; j++) {
cin >> v[i][j];
}
v[i][1]--;
v[i][2]--;
}
sort(v.begin(), v.end(), [&](auto x, auto y) { return x[1] < y[1]; });
vector<int> ans(n);
iota(ans.rbegin(), ans.rend(), 1);
for (auto [t, l, r] : v) {
if (t == 1) {
for (int i = l + 1; i <= r; i++) ans[i] = ans[i - 1];
}
}
for (auto [t, l, r] : v) {
if (t == 0) {
if (ans[l] == ans[r]) {
cout << "NO\n";
return 0;
}
}
}
cout << "YES\n";
for (int i = 0; i < n; i++) {
cout << ans[i] << " \n"[i == n];
}
}
1187E - Tree Painting(2100)
在一棵初始全白的树上,每次染黑一个与已有黑点相邻的白点(第一步可任选一点),并获得该点所在白色连通块大小的得分,求将全树染黑所能获得的最大总得分。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<vector<int>> adj(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> dep(n), sz(n);
auto dfs1 = [&](this auto&& self, int x, int p) -> void {
sz[x] = 1;
for (auto y : adj[x]) {
if (y != p) {
dep[y] = dep[x] + 1;
self(y, x);
sz[x] += sz[y];
}
}
};
dep[0] = 1;
dfs1(0, -1);
vector<ll> dp(n);
dp[0] = accumulate(dep.begin(), dep.end(), 0LL);
auto dfs2 = [&](this auto&& self, int x, int p) -> void {
for (auto y : adj[x]) {
if (y != p) {
dp[y] = dp[x] - 2 * sz[y] + n;
self(y, x);
}
}
};
dfs2(0, -1);
cout << *max_element(dp.begin(), dp.end()) << '\n';
}
1157D - N Problems During K Days(1900)
给定正整数 \(n\) 和 \(k\),要求构造一个长度为 \(k\)、元素之和为 \(n\) 的正整数序列 \(a\),且满足相邻两项 \(a_i < a_{i+1} \le 2a_i\),若无法构造则输出无解。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll n, k;
cin >> n >> k;
if (k == 1) {
cout << "YES\n" << n << '\n';
return 0;
}
n -= k * (k + 1) / 2;
if (n < 0) {
cout << "NO\n";
return 0;
}
if (n == k - 1) {
if (k == 2 || k == 3) {
cout << "NO\n";
} else {
cout << "YES\n1 2 ";
for (int i = 0; i < k - 3; i++) cout << i + 4 << ' ';
cout << k + 2 << '\n';
}
return 0;
}
ll a0 = n / k + 1;
ll r = n % k;
cout << "YES\n";
for (int i = 0; i < k - r; i++) cout << a0++ << ' ';
a0++;
for (int i = 0; i < r; i++) cout << a0++ << ' ';
}
1157E - Minimum Array(1700)
给定两个长度为 \(n\) 的数组 \(a\) 和 \(b\),你需要重新排列数组 \(b\) 的元素,使得对应位置相加并对 \(n\) 取模生成的新数组 \(c\)(即 \(c_i = (a_i + b_i) \pmod n\))的字典序最小。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
multiset<int> ms;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
int x;
cin >> x;
ms.insert(x);
}
for (int i = 0; i < n; i++) {
auto ite = ms.lower_bound(n - a[i]);
if (ite == ms.end()) {
ite = ms.begin();
}
cout << (a[i] + *ite) % n << " \n"[i + 1 == n];
ms.erase(ite);
}
}
1157F - Maximum Balanced Circle(2000)
从给定数组中选出最多的数排成一个环,使得环上任意相邻两数之差的绝对值都不超过 \(1\)。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a;
map<int, int> mp;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
if (++mp[x] == 1) {
a.push_back(x);
}
}
sort(a.begin(), a.end());
int max_len = 0, start, end;
for (int i = 0, j; i < a.size(); i = max(i + 1, i + j - a[i])) {
j = a[i];
while (mp[j] >= 2) j++;
int len = j - a[i];
if (len > 0) {
int cnt = 0;
for (int k = a[i] - 1; k <= j; k++) cnt += mp[k];
if (cnt > max_len) {
max_len = cnt;
start = a[i] - int(mp[a[i] - 1] > 0);
end = j - int(mp[j] == 0);
}
}
}
if (max_len > 0) {
deque<int> ans;
for (int i = start; i <= end; i++) {
int cnt = mp[i];
while (cnt >= 2) {
ans.push_back(i);
ans.push_front(i);
cnt -= 2;
}
if (cnt >= 1) {
ans.push_back(i);
}
}
cout << ans.size() << '\n';
for (int e : ans) cout << e << ' ';
return 0;
}
for (int i = 0; i + 1 < a.size(); i++) {
if (a[i] + 1 == a[i + 1]) {
cout << "2\n";
cout << a[i] << ' ' << a[i] + 1 << '\n';
return 0;
}
}
cout << "1\n" << a[0] << '\n';
}
1155D - Beautiful Array(1900)
给定一个数组和一个整数 \(x\),你可以最多将其中一段连续子数组的所有元素乘以 \(x\),求操作后整个数组的最大连续子数组和。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, x;
cin >> n >> x;
ll dp0 = 0, dp1 = INT_MIN, dp2 = INT_MIN, ans = 0;
for (int i = 0; i < n; i++) {
ll a;
cin >> a;
ll nxt0 = max({a, a + dp0});
ll nxt1 = max({a * x, a * x + dp0, a * x + dp1});
ll nxt2 = max({a + dp1, a + dp2});
dp0 = nxt0;
dp1 = nxt1;
dp2 = nxt2;
ans = max({ans, dp0, dp1, dp2});
}
cout << ans << '\n';
}
1092C - Prefixes and Suffixes(1700)
给定一个未知字符串被打乱顺序的所有真前缀和真后缀(长度从 \(1\) 到 \(n-1\)),要求将每个输入的子串正确分类为前缀('P')或后缀('S')。
题解
关键是判断长度是 \(n-1\) 的两个真前缀或真后缀到底谁是真前缀/真后缀,这样就知道整个串的内容了
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<string> v(2 * n - 2);
string s1, s2;
for (int i = 0; i < 2 * n - 2; i++) {
cin >> v[i];
if (v[i].size() == n - 1) {
if (s1.empty()) {
s1 = s2 = v[i];
} else {
s1 += v[i].back();
s2 = v[i][0] + s2;
}
}
}
auto solve = [&](string s) {
string ans;
vector<int> vis(n);
for (auto a : v) {
int len = a.size();
if (!vis[len] && s.substr(0, len) == a) {
ans += 'P';
vis[len] = 1;
} else if (s.substr(n - len, len) == a) {
ans += 'S';
} else {
return ""s;
}
}
return ans;
};
string ans1 = solve(s1);
if (ans1.size()) {
cout << ans1 << '\n';
} else {
cout << solve(s2) << '\n';
}
}
1092E - Minimal Diameter Forest(2000)
给定一个森林,要求添加边将其连通成一棵树,并使得最终生成的这棵树的直径最小。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<vector<int>> g(n);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
u--, v--;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> dep(n), par(n);
auto dfs = [&](this auto&& self, int x, int p, int d) -> array<int, 2> {
dep[x] = d;
par[x] = p;
array<int, 2> rt = {dep[x], x};
for (auto y : g[x]) {
if (y != p) {
rt = max(rt, self(y, x, d + 1));
}
}
return rt;
};
auto find_g = [&](int x) -> array<int, 2> {
auto fp = dfs(x, -1, 1);
fp = dfs(fp[1], -1, 1);
int u = fp[1];
for (int j = 0; j < fp[0] / 2; j++) {
u = par[u];
}
return {fp[0], u};
};
vector<array<int, 2>> gs;
for (int i = 0; i < n; i++) {
if (dep[i] == 0) {
gs.push_back(find_g(i));
}
}
sort(gs.begin(), gs.end(), greater<>());
vector<array<int, 2>> ans;
for (int i = 1; i < gs.size(); i++) {
auto [u, v] = array{gs[0][1], gs[i][1]};
g[u].push_back(v);
g[v].push_back(u);
ans.push_back({u, v});
}
cout << find_g(0)[0] - 1 << '\n';
for (auto [u, v] : ans) {
cout << u + 1 << ' ' << v + 1 << '\n';
}
}
1092F - Tree with Maximum Cost(1900)
给定一棵带点权的树,求选择一个中心节点,使得所有节点到该节点的距离与其自身权值乘积的总和(即 \(\sum dist(i,v) \cdot a_i\))最大。
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
ll sum_a = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
sum_a += a[i];
}
vector<vector<int>> edge(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
edge[u].push_back(v);
edge[v].push_back(u);
}
vector<ll> sw(n), sdw(n);
auto dfs1 = [&](this auto&& self, int x, int p) -> void {
sw[x] = a[x];
for (auto y : edge[x]) {
if (y != p) {
self(y, x);
sw[x] += sw[y];
sdw[x] += sw[y] + sdw[y];
}
}
};
dfs1(0, -1);
auto dfs2 = [&](this auto&& self, int x, int p) -> void {
for (auto y : edge[x]) {
if (y != p) {
sdw[y] += sdw[x] - (sdw[y] + sw[y]) + (sum_a - sw[y]);
self(y, x);
}
}
};
dfs2(0, -1);
cout << *max_element(sdw.begin(), sdw.end()) << '\n';
}

浙公网安备 33010602011771号