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;
}