Codeforces Round 1016 (Div. 3)
A. Ideal Generator
题意:给你\(k\),求能不能使得所有的\(n \leq k\)的\(n\),都能构造一个全部非零的长度为\(k\)的总和为\(n\)的回文数组。
如果\(k\)是奇数,都放\(1\)然后剩下的给中间就行了。
如果是偶数,无解。
点击查看代码
void solve() {
int n;
std::cin >> n;
if (n & 1) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
B. Expensive Number
题意:\(n\)的价值为\(n\)除数位和。你可以删去\(n\)的一些数,使得它严格大于\(0\),可以有前导零。使得价值最小需要删几个数。
显然最小价值为\(1\)。那么如果我们留下一个非零数,那么它前面的\(0\)可以保留。求前面零最多的数。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int n = s.size();
int ans = n - 1;
int cnt = 0;
for (int i = 0; i < n; ++ i) {
if (s[i] != '0') {
ans = std::min(ans, n - 1 - cnt);
} else {
++ cnt;
}
}
std::cout << ans << "\n";
}
C. Simple Repetition
题意:把\(n\)复制\(k\)次形成一个数字,求这个数字是不是质数。
如果\(n\)不是\(1\)而\(k>1\),肯定不是质数,因为\(nn...nn\)显然可以整除\(n\)。
那么如果\(n\)是\(1\),复制\(k\)遍判断,其它情况就是\(k=1\),也是直接判断是不是质数。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
if (n != 1 && k > 1) {
std::cout << "NO\n";
} else {
if (n == 1) {
while ( -- k) {
n = n * 10 + 1;
}
}
if (n == 1) {
std::cout << "NO\n";
return;
}
for (int i = 2; i <= n / i; ++ i) {
if (n % i == 0) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
}
}
D. Skibidi Table
题意:一个\(2^n \times 2^n\)的矩阵,把\([1, 2^n \times 2^n]\)的数放进去,把数组分成四部分。顺序为先放左上角,然后放右下角,再放左下角,最后放右上角。这是一个递归,因为每一部分是一个\(2^{n-1} \times 2^{n-1}\)的矩阵,这个矩阵也按照这个规律放置。现在有两种询问,一种是问\((x, y)\)这个位置是哪个数,一种是问\(d\)的坐标。
题目已经很明显提示这是递归了。
对于第一种询问,我们递归求解,\(dfs(n, x, y)\)表示\(2^n \times 2^n\)的矩阵求\((x, y)\)这个位置的数。那么记\(m = 2^{n-1}\),如果\(x \leq m, y \leq m\),则在左上角,答案是\(dfs(n - 1, x, y)\)。如果\(x > m, y > m\),那么在右下角,答案是\(dfs(n - 1, x - m, y - m) + m^2\),意味把它放缩到\(2^{n-1} \times 2^{n-1}\)的矩阵里下标是\((x-m, y-m)\),同时要加上左下角的数。那么同理讨论,如果\(x > m, y leq m\),则在左下角,答案为\(dfs(n - 1, x - m, y) + 2 \times m^2\)。否则答案为\(dfs(n - 1, x, y - m) + 3 \times m^2\)。边界就是\(n=0\)返回\(1\)。
第二种询问同理讨论,\(dfs(n, d)\)表示\(2^n \times 2^n\)的矩阵求\(d\)的坐标,那么讨论在哪个部分,同时减去相应的值,得到下标后加上对应的值。
具体参考代码。
点击查看代码
void solve() {
i64 n, q;
std::cin >> n >> q;
auto work1 = [&](auto & self, i64 n, i64 x, i64 y) -> i64 {
if (n == 0) {
return 1;
}
i64 m = 1ll << n - 1;
if (x <= m && y <= m) {
return self(self, n - 1, x, y);
} else if (x > m && y > m) {
return self(self, n - 1, x - m, y - m) + m * m;
} else if (x > m && y <= m) {
return self(self, n - 1, x - m, y) + m * m * 2;
} else {
return self(self, n - 1, x, y - m) + m * m * 3;
}
};
auto work2 = [&](auto & self, i64 n, i64 d) -> std::pair<i64, i64> {
if (n == 0) {
return {1, 1};
}
i64 m = 1ll << n - 1;
if (d <= m * m) {
return self(self, n - 1, d);
} else if (d <= 2 * m * m) {
auto [x, y] = self(self, n - 1, d - m * m);
x += m; y += m;
return {x, y};
} else if (d <= 3 * m * m) {
auto [x, y] = self(self, n - 1, d - 2 * m * m);
x += m;
return {x, y};
} else {
auto [x, y] = self(self, n - 1, d - 3 * m * m);
y += m;
return {x, y};
}
};
while (q -- ) {
std::string op;
std::cin >> op;
if (op[0] == '-') {
i64 x, y;
std::cin >> x >> y;
std::cout << work1(work1, n, x, y) << "\n";
} else {
i64 d;
std::cin >> d;
auto [x, y] = work2(work2, n, d);
std::cout << x << " " << y << "\n";
}
}
}
E. Min Max MEX
题意:把\(a\)分成\(k\)份,价值为\(\min_{i=1}^{k} (mex(b_i))\),求最大价值。
考虑二分,如果\(x\)可行,那么我们从前往后遍历求\(mex\),一直到\(mex\)大于等于\(x\)停止,那么如果能得到至少\(k\)份就是可以的。因为后面多出来的直接给最后一部分不会减少这一部分的\(mex\)。
upd:用\(map\)双log被叉了。\(check\)里我们开个数组记录每个数出现没有就行,每次找到一段把这一段的数的贡献消去就行了。这样每个数最多被遍历两次。
点击查看代码
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];
}
if (n == k) {
std::cout << (std::ranges::count(a, 0) == n) << "\n";
return;
}
auto check = [&](int x) -> bool {
int cnt = 0;
std::vector<int> st(x + 1);
for (int i = 0; i < n; ++ i) {
int mex = 0;
int j = i;
for (; j < n && mex < x; ++ j) {
if (a[j] < x) {
st[a[j]] = 1;
}
while (st[mex]) {
++ mex;
}
}
cnt += mex >= x;
for (int k = i; k < j; ++ k) {
if (a[k] < x) {
st[a[k]] = 0;
}
}
i = j - 1;
}
return cnt >= k;
};
int l = 0, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) {
l = mid;
} else {
r = mid - 1;
}
}
std::cout << l << "\n";
}
F. Hackers and Neural Networks
这题不给样例解释,题目看半天。
题意:你要得到数组\(a\),一开始你有一个长度为\(n\)元素都为空的数组\(c\)。给你\(m\)个长度为\(n\)的数组\(b_i\)。你可以进行若干个操作,每次选择其中一种:
- 选择一个\(i\),那么会随机选择一个空位\(j\),使得\(c[j] = b[i][j]\)
- 选择一个\(i\),然后在选择一个\(j\),使得\(c[j]\)为空。
因为是随机的,所有我们不能保证我们得到想要的。但我们可以对一个数组进行\(n\)次第一种操作,那么就变成了这个数组,然后我们把错误的位置变成空,再和其它数组进行第一个操作。
那么我们枚举一开始得到的数组,一开始操作\(n\)次。然后每次贪心找剩下可以得到最多正确位置的\(b_i\),假设有\(cnt\)个,那么我们把这\(cnt\)个位置变成空,然后操作\(cnt\)次进行,一共操作\(2\times cnt\)次。
一直到所有位置都满足条件就行了。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::string> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector b(m, std::vector<int>(n));
for (int i = 0; i < m; ++ i) {
for (int j = 0; j < n; ++ j) {
std::string s;
std::cin >> s;
b[i][j] = s == a[j];
}
}
auto work = [&](int i) -> int {
int sum = 0;
std::set<int> s;
for (int i = 0; i < n; ++ i) {
s.insert(i);
}
auto get = [&]() -> std::vector<int> {
int max = 0, id = 0;
for (int i = 0; i < m; ++ i) {
int cnt = 0;
for (auto & j : s) {
cnt += b[i][j];
}
if (cnt > max) {
max = i;
id = i;
}
}
std::vector<int> res;
for (auto & i : s) {
if (b[id][i]) {
res.push_back(i);
}
}
return res;
};
for (int j = 0; j < n; ++ j) {
if (b[i][j]) {
s.erase(j);
}
}
sum = n;
while (s.size()) {
auto c = get();
if (c.empty()) {
return - 1;
}
for (auto & x : c) {
s.erase(x);
}
sum += (int)c.size() * 2;
}
return sum;
};
int ans = -1;
for (int i = 0; i < m; ++ i) {
int v = work(i);
if (ans == -1 || v < ans) {
ans = v;
}
}
std::cout << ans << "\n";
}
G. Shorten the Array
题意:给你一个数组\(a\),求一对数\((i, j)\),使得\(a_i \oplus a_j \geq k\)且\(j - i + 1\)最小。
不明白为什么\(g\)是一个\(01trie\)板子。
我们给\(01trie\)每个节点记录一个最大下标,表示他这个节点的前缀可以表示的最后的\(a_i\)。那么我们从前往后插入,这个下标是递增的,直接更改就行。
然后就是查询,对于\(a_i\),我们要求一个\(a_j \oplus a_i \leq k\)且\(j\)最大,那么从高位开始考虑,如果这一位\(a_i\)是\(x\),\(k\)是\(y\),如果\(y=1\),那么跳到\(a\oplus 1\)这个子树,否则我们可以在一位大于\(k\),用\(a \oplus 1\)这棵子树的最大下标更新答案。然后跳到\(a\)这棵子树,和\(k\)保持一个相等的前缀。这样就能得到最后边的\(j\)。
点击查看代码
const int N = 2e5 + 5;
int trie[N * 30][2];
int node_max[N * 30];
struct TrieWith01 {
int idx;
int creat() {
memset(trie[idx], 0, sizeof trie[idx]);
node_max[idx] = -1;
return idx ++ ;
}
TrieWith01() {
idx = 0;
creat();
}
void insert(int x, int id) {
int p = 0;
for (int i = 30; i >= 0; -- i) {
int s = x >> i & 1;
if (!trie[p][s]) {
trie[p][s] = creat();
}
p = trie[p][s];
node_max[p] = id;
}
}
int query(int x, int k) {
int p = 0;
int res = -1;
for (int i = 30; i >= 0; -- i) {
int a = x >> i & 1, b = k >> i & 1;
if (b == 1) {
p = trie[p][a ^ 1];
} else {
res = std::max(res, node_max[trie[p][a ^ 1]]);
p = trie[p][a];
}
if (p == 0) {
return res;
}
}
res = std::max(res, node_max[p]);
return res;
}
};
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];
}
TrieWith01 tr;
int ans = -1;
for (int i = 0; i < n; ++ i) {
tr.insert(a[i], i);
int j = tr.query(a[i], k);
if (j != -1) {
if (ans == -1 || i - j + 1 < ans) {
ans = i - j + 1;
}
}
}
std::cout << ans << "\n";
}

浙公网安备 33010602011771号