1. 程式人生 > 其它 >Deltix Round, Autumn 2021 (open for everyone, rated, Div. 1 + Div. 2)

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;

#define int long long
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];
int32_t 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;
}