Deltix Round, Autumn 2021 (open for everyone, rated, Div. 1 + Div. 2)
Deltix Round, Autumn 2021 (open for everyone, rated, Div. 1 + Div. 2)
A. Divide and Multiply
可以发现,每次只让一个数乘是最优的。因为范围很小,所以可以枚举将哪个数乘\(2\),注意答案为指数级别要开\(long~long\)。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 15 + 5;
int a[MAXN], b[MAXN];
int32_t main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
int res = 0;
for (int i = 1; i <= n; ++i) {
memcpy(b, a, sizeof(a));
for (int j = 1; j <= n; ++j) {
if (i == j) continue;
while (b[j] % 2 == 0) {
b[j] >>= 1;
b[i] <<= 1;
}
}
int sum = 0;
for (int j = 1; j <= n; ++j) {
sum += b[j];
}
res = max(res, sum);
}
cout << res << '\n';
}
// system("pause");
return 0;
}
B. William the Vigilant
可以发现答案就是\(abc\)的数量,那么对于每次修改我们暴力计算包括这个位置的子串即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
char s[MAXN];
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m >> s + 1;
// 判断[x, x + 2]的子串是否为abc
auto check = [&](int x) -> bool {
return 1 <= x && x + 2 <= n && s[x] == 'a' && s[x + 1] == 'b' && s[x + 2] == 'c';
};
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (check(i)) {
++cnt;
}
}
while (m--) {
int p;
char c;
cin >> p >> c;
for (int i = p - 2; i <= p; ++i) {
cnt -= check(i);
}
s[p] = c;
for (int i = p - 2; i <= p; ++i) {
cnt += check(i);
}
cout << cnt << '\n';
}
// system("pause");
return 0;
}
C. Complex Market Analysis
要让子数组相乘为质数,那么最多只有一个质数,其他数都为\(1\)。那么对于每个质数我们只需暴力向左向右找最多连续的\(1\)即可,根据乘法原理相乘即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e6 + 5;
bool isNotP[MAXN];
int p[MAXN], cnt;
void seive() {
isNotP[1] = true;
for (int i = 2; i < MAXN; ++i) {
if (!isNotP[i]) {
p[++cnt] = i;
}
for (int j = 1; j <= cnt && i * p[j] < MAXN; ++j) {
isNotP[i * p[j]] = true;
if (i % p[j] == 0) {
break;
}
}
}
}
int a[MAXN];
int32_t main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
seive();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
int res = 0;
for (int i = 1; i <= n; ++i) {
if (isNotP[a[i]]) continue;
int l = 0;
for (int j = i + k; j <= n; j += k) {
if (a[j] != 1) {
break;
}
++l;
}
int r = 0;
for (int j = i - k; j >= 1; j -= k) {
if (a[j] != 1) {
break;
}
++r;
}
// 当前位置贡献为: 只包含左边1的子序列 + 只包含右边1的子序列 + 包含左右1的子序列
res += l + r + l * r;
}
cout << res << '\n';
}
// system("pause");
return 0;
}
D. Social Network
本题描述有点复杂,实际上就是一个并查集模拟题。
首先给出\(m\)对限制条件,对于每个\(i ~ \epsilon ~ [1, m]\),我们可以让任意\(i\)对人互相认识,即并查集中的合并,但是最后你的并查集要满足前\(i\)个限制条件,要求可能的并查集的最大连通块的大小。
对于题目给的第二个样例,当我们需要满足前四个限制条件时,我们首先将\([1,2],[2,3],[3,4]\)合并,此时我们还可以指定\(1\)个关系,那么肯定是连通块最大的优先指定,因为其他连通块都是\(1\),我们随便指定一对关系即可\((例如[4,5])\),那么此时\(1\)最多认识\(4\)个人。
那么我们可以分两种情况讨论,假设当前要判断第\(i\)对关系\(x_i,y_i\):
\(\bullet\) 如果\(x_i\)和\(y_i\)已经在同一块中了,此时我们可以随意指定一对关系,为了让答案最大,我们应该优先指定连通块大的。
\(\bullet\) 否则我们只能先让他们合并以满足题目限制条件。
因为要满足前\(i\)个限制条件,我们要先满足前\(i-1\)个限制条件,所以我们可以枚举满足条件个数,假如当前限制条件我们没满足即不在同一个块中,我们先合并它们,否则我们用一个计数器\(scc\)记录我们可以随意指定的次数。对于每次询问,我们暴力遍历这个集合,找到所有连通块的大小并从大到小排序选出前\(scc\)个。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 5;
int fa[MAXN], sz[MAXN];
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return false;
}
fa[x] = y;
sz[y] += sz[x];
return true;
}
int X[MAXN], Y[MAXN];
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
fa[i] = i;
sz[i] = 1;
}
for (int i = 1; i <= m; ++i) {
cin >> X[i] >> Y[i];
}
// scc记录可以任意指定几次
int scc = 0;
for (int i = 1; i <= m; ++i) {
int x = X[i], y = Y[i];
// 合并失败即两个人认识时计数器+1
if (!merge(x, y)) {
++scc;
}
vector<int> a;
for (int j = 1; j <= n; ++j) {
if (j == find(j)) {
a.push_back(sz[j]);
}
}
sort(a.begin(), a.end(), greater<int>());
int res = 0;
for (int j = 0; j < min((int)a.size(), scc + 1); ++j) {
res += a[j];
}
cout << res - 1 << '\n';
}
// system("pause");
return 0;
}
E. William The Oblivious
和\(b\)题不同,本题要满足的是子序列不为\(abc\)而不是子串。
考虑线段树解决本题,本题最复杂的地方就是上推部分,我们用\(abc\)表示没有\(abc\)情况的最小修改次数,\(ab,bc\)等同理。
首先我们解决只有\(ab\)的字符串要满足没有\(ab\)的情况,假设当前节点为\(p\),如果我们已经知道了右儿子的\(ab\),即右边只会有\(ba\)的形式,那么我们只需要把左儿子的所有\(a\)变成\(b\)即可,如果已经知道了左儿子\(ab\),只需要把右儿子的所有\(b\)全改成\(a\)即可,即\(tree[p].ab = min(tree[ls(p)].a + tree[rs(p)].ab, tree[ls(p)].ab + tree[rs(p)].b)\)。
\(ac,bc\)可以类似的求出来,最后我们需要计算的为\(abc\),同理,如果我们知道了右儿子的\(abc\),那么右边没有\(abc\)但是可能有\(acb,bac,bca,cba,cab\)的情况,因为右边可能有\(bc\)这样的子序列,所以我们要修改左儿子的所有\(a\)。其他情况同理。
因为是单点修改,所以每次修改后暴力上推即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e5 + 5;
char s[MAXN];
#define ls(x) x << 1
#define rs(x) x << 1 | 1
struct Node {
int l, r;
int a, b, c, ab, ac, bc, abc;
int mid() {
return (l + r) >> 1;
}
} tree[MAXN << 2];
void pushup(int p) {
tree[p].a = tree[ls(p)].a + tree[rs(p)].a;
tree[p].b = tree[ls(p)].b + tree[rs(p)].b;
tree[p].c = tree[ls(p)].c + tree[rs(p)].c;
tree[p].ab = min(tree[ls(p)].a + tree[rs(p)].ab, tree[ls(p)].ab + tree[rs(p)].b);
tree[p].bc = min(tree[ls(p)].b + tree[rs(p)].bc, tree[ls(p)].bc + tree[rs(p)].c);
tree[p].abc = min({
tree[ls(p)].a + tree[rs(p)].abc,
tree[ls(p)].ab + tree[rs(p)].bc,
tree[ls(p)].abc + tree[rs(p)].c
});
}
void build(int l, int r, int p) {
tree[p].l = l, tree[p].r = r;
if (l == r) {
tree[p].a = (s[l] == 'a');
tree[p].b = (s[l] == 'b');
tree[p].c = (s[l] == 'c');
return ;
}
int mid = (l + r) >> 1;
build(l, mid, ls(p));
build(mid + 1, r, rs(p));
pushup(p);
}
void modify(int l, int r, char c, int p) {
if (l <= tree[p].l && tree[p].r <= r) {
s[l] = c;
tree[p].a = (s[l] == 'a');
tree[p].b = (s[l] == 'b');
tree[p].c = (s[l] == 'c');
return ;
}
int mid = tree[p].mid();
if (l <= mid) modify(l, r, c, ls(p));
if (r > mid) modify(l, r, c, rs(p));
pushup(p);
}
int32_t main(int argc, char *argv[]) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m >> s + 1;
build(1, n, 1);
while (m--) {
int p;
char c;
cin >> p >> c;
modify(p, p, c, 1);
cout << tree[1].abc << '\n';
}
// system("pause");
return 0;
}

浙公网安备 33010602011771号