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;
}
posted @ 2021-12-01 19:32  stler  阅读(163)  评论(0)    收藏  举报