Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2)
A. Mix Mex Max
题意:一个数组,有些地方没填。你需要给这些位置填上数使得数组里任意连续的三个数的\(mex = max - min\)。
当\(mex = 0\)时,\(max = min\);当\(mex \ne 0\)时,\(min = 0, max - min = max\)。而\(mex \ne max\)。所以只有\(mex = 0\)且三个数都相同才行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::set<int> s;
for (int i = 0; i < n; ++ i) {
if (a[i] != -1) {
s.insert(a[i]);
}
}
if (s.size() > 1 || (s.size() == 1 && *s.begin() == 0)) {
std::cout << "NO\n";
} else {
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;
}
B. Hamiiid, Haaamid... Hamid?
题意:有\(n\)个点,有些地方是墙,有些地方是空地。你一开始在一个空地上坐标是\(x\),每回合另一个人会在除你所在的空地外的其它空地建一面墙,然后你可以选择向左或向右,走到你选择的方向距离你最近的墙的位置并且把这个墙摧毁。如果这个方向没有墙挡住你就胜利。求回合数。
方向是一开始选择后就不会变的。因为左右切换撞的墙一定比往一个方向走撞的多。
那么答案最差是\(\min(x, n - x + 1)\),然后如果一开始另一个人没有把某一边挨着\(x\)的位置变成墙,则可以直接跑到这一边最近的墙的位置,设左边最近墙的位置为\(l\),右边最近墙位置为\(r\)。如果一开始把\(x-1\)变成墙,那么往右走答案是\(n-r+2\),如果一开始把\(x+\)变成墙,答案是\(l+1\),另一个人肯定在这两个情况里选最大的,所以\(ans = \min(ans, \max(n - r + 2, l + 1))\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, x;
std::cin >> n >> x;
-- x;
std::string s;
std::cin >> s;
int l = x, r = x;
while (l >= 0 && s[l] == '.') {
-- l;
}
while (r < n && s[r] == '.') {
++ r;
}
int ans = std::min(x + 1, n - x);
int max = std::max(n - r + 1, l + 2);
ans = std::min(ans, max);
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. Trip Shopping
题意:两个数组\(a, b\),你要进行\(n\)次操作,每次选择两个位置\(i, j\),然后另一个人随意交换\(a_i, a_j, b_i, b_j\)这四个数。最终答案为\(\sum_{i=1}^{n} |a_i - a_j|\)。你需要答案尽可能小,另一个希望答案尽可能大,问最终答案。
最优操作是始终只交换一对\(i, j\)。也就是应该选\(i, j\)操作\(k\)次。因为另一个人肯定是拿最大数减最小数,次大数减次小数贡献最大,所以我们选择大于\(1\)对位置只会使得答案变大。
那么如果有\((a_i, b_i)\)和\((a_j, b_j)\)满足\(a_i \leq a_j \leq b_j \leq b_i\)或\(a_i \leq a_j \leq b_i \leq b_j\),则它们已经达到了最大值,另一个不管怎么交换也不会使得答案变大了,所以我们我们按\(a_i\)排序,记录前缀最大的\(b_i\),如果\(max_{b} \geq a_i\)则找到了这样的一对,直接输出答案。
否则任意一对都没有交集,也就是\(a_i \leq b_i \leq a_j \leq b_j\)。此时选择\(a_j - b_i\)最小的,答案增加\((a_j - b_i) \times 2\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<i64> 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];
}
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
ans += std::abs(a[i] - b[i]);
}
std::vector<std::pair<i64, i64>> c(n);
for (int i = 0; i < n; ++ i) {
c[i] = {std::min(a[i], b[i]), std::max(a[i], b[i])};
}
std::ranges::sort(c, [&](std::pair<i64, i64> & a, std::pair<i64, i64> & b) {
if (a.first == b.first) {
return a.second > b.second;
}
return a.first < b.first;
});
i64 min = 1e18;
i64 max = -1e18;
for (int i = 0; i < n; ++ i) {
auto & [x, y] = c[i];
if (max >= x) {
std::cout << ans << "\n";
return;
}
min = std::min(min, (x - max) * 2);
max = std::max(max, y);
}
std::cout << ans + min << "\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. Root was Built by Love, Broken by Destiny
题意:一个图,你要把它们分成一个二分图,然后两边任意排列点的位置,满足所有边不能相交。有多少方案。
模拟发现,图不仅需要是二分图,还不能有环。
而题目又保证图联通,所以这个图一定是一棵树。
然后一个点和它相邻的点一定在不同侧,为了不和其它边相交,这些点应该连续排列,然后可能这些点和同一侧的其它点也有交集,此时这个点必须放在外侧,所以最多有两个和其它点有边的点,这样两个点可以放在左右两侧,其它点在中间随意排列。
然后每个方案还可以两侧交换,依然是合法方案,所以方案数乘二。
然后因为还有前后关系,我们可以左右翻转一个方案,所以答案又可以乘二。不过需要注意的是如果一侧只有一个点,左右翻转还是一样的方案,此时不能乘二。
代码省略取模类。
点击查看代码
constexpr int P = 1000000007;
using Z = MInt<P>;
struct Comb {
int n;
std::vector<Z> _fac;
std::vector<Z> _invfac;
std::vector<Z> _inv;
Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
Comb(int n) : Comb() {
init(n);
}
void init(int m) {
if (m <= n) return;
_fac.resize(m + 1);
_invfac.resize(m + 1);
_inv.resize(m + 1);
for (int i = n + 1; i <= m; i++) {
_fac[i] = _fac[i - 1] * i;
}
_invfac[m] = _fac[m].inv();
for (int i = m; i > n; i--) {
_invfac[i - 1] = _invfac[i] * i;
_inv[i] = _invfac[i] * _fac[i - 1];
}
n = m;
}
Z fac(int m) {
if (m > n) init(2 * m);
return _fac[m];
}
Z invfac(int m) {
if (m > n) init(2 * m);
return _invfac[m];
}
Z inv(int m) {
if (m > n) init(2 * m);
return _inv[m];
}
Z binom(int n, int m) {
if (n < m || m < 0) return 0;
return fac(n) * invfac(m) * invfac(n - m);
}
} comb;
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
if (m != n - 1) {
std::cout << 0 << "\n";
return;
}
Z ans = 1;
for (int i = 0; i < n; ++ i) {
int cnt = 0;
for (auto & j : adj[i]) {
cnt += adj[j].size() == 1;
}
if (cnt + 2 < adj[i].size()) {
ans = 0;
} else {
ans *= comb.fac(cnt);
}
}
ans *= 2;
int max = 0;
for (int i = 0; i < n; ++ i) {
max = std::max(max, (int)adj[i].size());
}
if (max != n - 1) {
ans *= 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;
}

浙公网安备 33010602011771号