Codeforces Round 1034 (Div. 3)
A. Blackboard Game
题意:有\(0\)到\(n-1\)这些数,两个人轮流拿数,第二个人拿的数和第一个人的相加模\(4\)要等于\(3\)。谁不能拿谁输。求第一个能不能赢。
观察发现四个四个一组,\(0, 3\)一对\(1, 2\)一对。发现只有不是\(4\)的倍数第二个人就有匹配不到的数。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
if (n % 4 == 0) {
std::cout << "Bob\n";
} else {
std::cout << "Alice\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. Tournament
题意:给你一个数组,每次取两个数出来讨论小的那一个,如果相等就淘汰任意一个。求第\(j\)个数能不能是剩下的\(k\)个数。
如果\(k=1\),那么这个数必须是最大的。否则我们用最大的吧其它数都淘汰,满足所有\(k\geq 2\)的情况。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, j, k;
std::cin >> n >> j >> k;
-- j;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
if (k >= 2 || a[j] == *std::max_element(a.begin(), a.end())) {
std::cout << "YES\n";
} else {
std::cout << "NO\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. Prefix Min and Suffix Max
题意:给你一个数组,你每次可以选择这个数组的一个前缀然后用前缀最小值替换这个前缀,或者选择一个后缀用后缀最大值替换这个后缀。对于第\(i\)个数求他能不能是最后一个数。
记录前缀最小值和后缀最大值,那么如果\(i\)是通过前缀操作留下来的,那么肯定有\(pre_{min} = a_i\),此时只需要后缀最大值大于前缀最小值就可以用一次前缀操作留下\(a_i\),否则是通过后缀操作留下的,同理有\(suf_max = a_i\),且\(pre_{min} \leq suf_{max}\)。
点击查看代码
#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::vector<int> suf(n + 1);
for (int i = n - 1; i >= 0; -- i) {
suf[i] = std::max(a[i], suf[i + 1]);
}
std::string ans(n, '0');
for (int i = 0, min = 2e9; i < n; ++ i) {
min = std::min(min, a[i]);
if ((min == a[i] && suf[i] >= min) || (suf[i] == a[i] && min <= suf[i])) {
ans[i] = '1';
}
}
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;
}
D. Binary String Battle
题意:两个人博弈,一个\(01\)串,第一个人每次任意选择\(k\)个位置把它们变成\(0\),第二个人每次选择连续的\(k\)个位置把它们变成\(1\)。求有没有一个时刻可以把所有位置变成\(0\)。
如果\(n < 2k\)可以赢。因为第二个人应该是在一个\(1\)旁边放\(k\)个\(1\)造成增加\(k\)个\(1\)的局面,如果第一个人不能遏制这种局面那么无解,想要遏制这种局面就得把这些\(1\)从两侧开始变,这时因为\(n < 2k\),第二个人想造连续的\(k\)个\(1\)一定就包含中间,无法继续增加\(k\)个,模拟发现第一个人几轮消耗后就能一次把所有\(1\)都消了。
还有一开始\(1\)的个数小于等于\(k\)个显然能赢。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
int cnt = 0;
for (int i = 0; i < n; ++ i) {
if (s[i] == '1') {
++ cnt;
}
}
if (n < 2 * k || cnt <= k) {
std::cout << "Alice\n";
return;
}
std::cout << "Bob\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. MEX Count
题意:给你一个数组,求对于\(k\in [0, n]\),删去\(k\)个数后可以得到的\(mex\)的数量。
反过来想,想得到\(mex = x\)需要删几个数?这里首先需要\([0, x - 1]\)都至少出现一次。然后至少把\(x\)删了,这时下界。然后我们可以把\([0, x - 1]\)多余的都删了,然后\([x + 1, n\)的数都删了,这是上界。那么就变成了一个区间加单点查询问题。
点击查看代码
#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);
}
void init(int n) {
tr.assign(n << 2, {});
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 {
i64 sum, len = 1;
};
struct Tag {
i64 add;
bool exist() {
return add != 0;
}
void clear() {
add = 0;
}
};
Info operator + (const Info & a, const Info & b) {
Info res{};
res.sum = a.sum + b.sum;
res.len = a.len + b.len;
return res;
}
Info operator + (const Info & a, const Tag & b) {
Info res{};
res.sum = a.sum + a.len * b.add;
res.len = a.len;
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);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<int> cnt(n + 1);
for (auto & x : a) {
++ cnt[x];
}
LazySegmentTree<Info, Tag> tr(n + 1);
for (int i = 0, sum = 0; i <= n; ++ i) {
int l = cnt[i], r = n - i;
tr.modify(l, std::min(r, n), Tag{1});
sum += cnt[i];
if (cnt[i] == 0) {
break;
}
}
for (int i = 0; i <= n; ++ i) {
std::cout << tr.query(i, i).sum << " \n"[i == 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. Minimize Fixed Points
题意:构造一个长度为\(n\)的排列\(p\),使得对于\(i > 1\)的位置都有\(gcd(p_i, i) > 1\)。且要求\(p_i - i\)的位置最少。
我们开始设\(p_i = i\),然后从大到小操作,每次找\(i\)的最大的因子和\(p_i\)换位置。那么如果\(p_i \neq i\),则\(p_i\)已经交换过了,但\(p_i\)一定还是\(i\)的倍数,那么也是\(i\)的最大因子的倍数,如果没有交换,就是\(p_i = i\),则和最大因子的交换位置也没有问题。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
std::vector<int> minp, primes;
void sieve(int n) {
primes.clear();
minp.assign(n + 1, 0);
for (int i = 2; i <= n; ++ i) {
if (minp[i] == 0) {
minp[i] = i;
primes.push_back(i);
}
for (auto & p : primes) {
if (p * i > n) {
break;
}
minp[p * i] = p;
if (minp[i] == p) {
break;
}
}
}
}
void solve() {
int n;
std::cin >> n;
std::vector<int> p(n + 1);
std::ranges::iota(p, 0);
for (int i = n; i > 1; -- i) {
if (minp[i] == i) {
continue;
}
int j = i / minp[i];
std::swap(p[j], p[i]);
}
std::vector<int> ans(n + 1);
for (int i = 1; i <= n; ++ i) {
ans[p[i]] = i;
}
for (int i = 1; i <= n; ++ i) {
std::cout << ans[i] << " \n"[i == n];
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
sieve(1e5);
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
G. Modular Sorting
题意:给你一个数组\(a\),每次要么把\(a_i = x\),要么查询一个\(k\),你可以对任意位置加任意个\(k\),然后对\(m\)取模,求能不能使得数组不递减,查询不会改变原数组。
对于一次操作 \(a[i] = (a[i] + k)\%m\),可以任意多次应用,相当于把 \(a[i]\) 限制在同余类 \(a[i] (mod\ d)\) 中循环,其中 \(d = gcd(k, m)\)。
那么对于一个\(k\),每个\(a[i]\)可以变成其同余类中的任意一个数。我们直接对\(a[i] \%= d\),就可以得到其中最小的数。我们设为\(b[i] = a[i] \% d\)。那么如果\(b[i] \leq b[i + 1]\),则\(b[i + 1]\)相比\(b[i]\)不需要多加一次\(k\),否则\(b[i] > b[i + 1]\),那么\(i+1\)还需要加\(k\)的次数比\(i\)要多一次。我们把次数记为\(cnt_d\),那么意味着\(cnt_d < \frac{m}{d}\),于是我们可以对每个\(m\)的因子\(d\)维护一个\(cnt_d\)。每次操作一只需要更新\((i, i + 1), (i - 1, i)\)的贡献,操作二直接判断就行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
std::vector<std::vector<int>> factor;
void init(int n) {
factor.assign(n + 1, {});
for (int i = 1; i <= n; ++ i) {
for (int j = i; j <= n; j += i) {
factor[j].push_back(i);
}
}
}
void solve() {
int n, m, q;
std::cin >> n >> m >> q;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int k = factor[m].size();
std::map<int, int> cnt;
for (int i = 0; i + 1 < n; ++ i) {
for (auto & d : factor[m]) {
if (a[i] % d > a[i + 1] % d) {
cnt[d] += 1;
}
}
}
while (q -- ) {
int op;
std::cin >> op;
if (op == 1) {
int p, x;
std::cin >> p >> x;
-- p;
if (p) {
for (auto & d : factor[m]) {
if (a[p - 1] % d > a[p] % d) {
cnt[d] -= 1;
}
if (a[p - 1] % d > x % d) {
cnt[d] += 1;
}
}
}
if (p + 1 < n) {
for (auto & d : factor[m]) {
if (a[p] % d > a[p + 1] % d) {
cnt[d] -= 1;
}
if (x % d > a[p + 1] % d) {
cnt[d] += 1;
}
}
}
a[p] = x;
} else {
int k;
std::cin >> k;
int d = std::gcd(k, m);
if (cnt[d] <= (m / d - 1)) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
init(5e5);
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}

浙公网安备 33010602011771号