前言
本題解部分思路來自於網路。
A - Cut
題目大意
有 \(n\) 張卡片疊在一起,從上到下給出 \(n\) 卡片的編號,將 \(k\) 張卡片從牌堆底部放到頂部後,從上到下輸出卡片的編號。
解題思路
按照題意模擬即可。
code
#include <bits/stdc++.h>
using namespace std;
int a[105];
int main() {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
for (int i = n - k + 1, I = 1; I <= n; I++, i++) {
printf("%d ", a[i]);
if (i == n) i = 0;
}
puts("");
return 0;
}
B - Decrease 2 max elements
題目大意
給定一個長度為 \(N\) 的正整數序列 \(A=(A_1,A_2,...,A_N)\) ,求在進行幾次操作之後序列 \(A\) 中只剩下了一個或更少的正整數。
- 將序列 \(A\) 降序排序,再將序列的前兩位減去 \(1\) 。
解題思路
因為資料較小,所以直接按題意模擬即可。
code
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
priority_queue<int> que;
for (int i = 1, a; i <= n; i++) {
scanf("%d", &a);
que.push(a);
}
int a, b, cnt = 0;
while (n > 1) {
a = que.top();
que.pop();
b = que.top();
que.pop();
if (a - 1 <= 0) n--;
if (b - 1 <= 0) n--;
que.push(a - 1);
que.push(b - 1);
cnt++;
}
printf("%d\n", cnt);
return 0;
}
C - Triple Attack
題目大意
有 \(N\) 個敵人排成一排,每個敵人都有固定的生命值。重複以下操作,直到所有敵人的生命值為 \(0\) 或更少。使用初始化為 \(0\) 的 \(T\) :
- \(T\) 增加 \(1\) ,然後攻擊最前面的生命值大於 \(0\) 的敵人。若 \(T\) 是三的倍數,則敵人生命值減少 \(3\) ,否則減少 \(1\) 。
求當所有敵人的生命值為 0 或更少時,\(T\) 的值。
解題思路
因為資料過大,所以這道題不能用模擬。
因為 \(T\) 每加 \(3\) ,敵人的生命值就少 \(5\) ,所以可以先從最前面的生命值大於 0 的敵人的生命值中扣去儘可能多的 \(5\) ,再模擬扣除剩下的生命值。
code
#include <bits/stdc++.h>
using namespace std;
int h[200005];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", h + i);
}
long long cnt = 0, t = 0;
for (int i = 1; i <= n; i++) {
if (t != 0) {
if (h[i] == 1) {
t--;
cnt++;
continue;
}
if (h[i] == 2) {
if (t == 1) {
cnt++;
t--;
continue;
} else {
cnt += 2;
t -= 2;
continue;
}
}
h[i] -= t + 2;
cnt += t;
t = 0;
}
if (h[i] <= 0) continue;
cnt += h[i] / 5 * 3;
h[i] %= 5;
if (h[i] <= 0) continue;
if (h[i] <= 3) {
t = 3 - h[i];
cnt += h[i];
} else {
t = 0;
cnt += 3;
}
}
printf("%lld\n", cnt);
return 0;
}
D - Minimum Steiner Tree
題目大意
給定一個圖和一個長度為 \(K\) 的序列 \(V=(V_1,V_2,...,V_k)\) 。考慮從圖中刪除一些節點和邊得到一棵樹,使得這棵樹中包含所有 \(K\) 個指定節點 \(V_1,V_2,...,V_n\) 且節點數最少。
解題思路
先從一個被指定的節點開始深度優先搜尋。遍歷到第 \(i\) 個節點時,如果這個節點下面沒有被指定的節點,則這個節點沒有必要存在,否則保留這個節點。
code
#include <bits/stdc++.h>
using namespace std;
vector<int> a[200005];
int tag[200005];
int ans = 0;
int dfs(int u, int fa) {
int flag = tag[u];
for (int i : a[u]) {
if (i == fa) continue;
flag |= dfs(i, u);
}
ans += flag;
return flag;
}
int main() {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1, t, b; i <= n - 1; i++) {
scanf("%d%d", &t, &b);
a[t].push_back(b);
a[b].push_back(t);
}
int root;
for (int i = 1; i <= k; i++) {
scanf("%d", &root);
tag[root] = 1;
}
dfs(root, 0);
printf("%d\n", ans);
return 0;
}
E - Train Delay
題目大意
給定 \(n\) 個城市, \(m\) 趟火車的始發城市和終到城市,預定始發時間和預定終到時間和第一趟火車的延誤時間。求每一趟火車延誤多少時間才能使得:
- 任意兩個數 \(i,j(1 \leqslant i < j \leqslant m)\) ,如果第 \(i\) 趟火車可以換乘第 \(j\) 趟火車,則延誤後第 \(i\) 趟火車也可以換乘第 \(j\) 趟火車。
解題思路
先生成一個長度為 \(2m\) 操作序列 \(M\) ,序列的每一位都代表一趟火車始發/終到。按事件的發生時間進行排序,終到記錄優先。
之後遍歷 \(M\) ,對於每一條記錄:
-
如果這條記錄是始發記錄,則這趟火車的延誤時間應該是這趟火車的始發城市目前最後一趟到達的火車的到達時間減去這趟火車的始發時間。
-
如果這條記錄是終到記錄,則這趟火車的延誤時間再前面已經計算,所以應當更新這趟火車的終到城市目前最後一趟到達火車的到達時間。
最後就得出了每一趟火車的延誤時間
code
#include <bits/stdc++.h>
using namespace std;
int a[200005], b[200005], s[200005], t[200005];
int x[200005], d[200005];
struct Node {
int id, st, time;
Node(int a, int b, int c)
: id(a)
, st(b)
, time(c) {}
Node()
: id(0)
, st(0)
, time(0) {}
};
vector<Node> q;
int main() {
int n, m;
scanf("%d%d%d", &n, &m, &x[1]);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d%d", a + i, b + i, s + i, t + i);
q.emplace_back(i, 0, s[i]);
q.emplace_back(i, 1, t[i]);
}
sort(q.begin(),
q.end(),
[](const Node &i, const Node &j) {
if (i.time != j.time) return i.time < j.time;
if (i.st != j.st) return i.st > j.st;
return i.id < j.id;
});
for (int i = 0; i < 2 * m; i++) {
if (q[i].st) {
d[b[q[i].id]] =
max(d[b[q[i].id]], q[i].time + x[q[i].id]);
} else {
x[q[i].id] =
max(x[q[i].id], d[a[q[i].id]] - s[q[i].id]);
}
}
for (int i = 2; i <= m; i++) {
printf("%d ", x[i]);
}
puts("");
return 0;
}
F - Dividing Game
題目大意
給定一個長度為 \(N\) 陣列 \(A=(A_1,A_2,...,A_N)\) ,兩個人 Anna
和 Bruno
用這個陣列玩遊戲。
他們輪流進行以下操作,Anna
先行。
- 從陣列 \(A\) 中選擇一個數,把這個數替換成他的一個因數,但這個因數不能是它本身。
如果某個人無法再進行操作,則這個人就輸給了另一個人。
求如果兩個人足夠聰明,則誰會獲勝。
解題思路
把一個數替換成它的一個因數也可以看作這個數除以一些他的質因數。所以可以將每一個數分解質因數,然後把每一個數的每一個質因子看成一塊石頭,這個問題就變成Nim博弈了。
code
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int d[100005];
int main() {
for (int i = 2; i <= 100005; i++) {
d[i] = 1;
for (int j = 2; j * j <= i; j++) {
if (i % j == 0) {
d[i] += d[i / j];
break;
}
}
}
int n;
scanf("%d", &n);
int ret = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
ret ^= d[a[i]];
}
if (ret) {
puts("Anna");
} else {
puts("Bruno");
}
return 0;
}
G - Add and Multiply Queries
題目大意
給定兩個長度為 \(N\) 的陣列 \(A\) 和 \(B\) ,有三種操作:
-
1 i x
將 \(A\) 陣列的第 \(i\) 位設定為 \(x\) 。 -
2 i x
將 \(B\) 陣列的第 \(i\) 位設定為 \(x\) 。 -
3 l r
解決一下問題並輸出答案:- 最初 \(v = 0\) ,對於 \(i = l,l + 1,...,r\) ,將 \(v\) 替換為 \(v + A_i\) 或 \(vB_i\) ,求 \(v\) 最大可能值是多少。
給定一個操作序列,按順序執行操作。
解題思路
這道題可以用線段樹,前兩個操作都可以用線段樹很方便的解決。
對於第三個操作,可以先用二分找到區間 \([l,r]\) 中第一個 \(B_i \geqslant 2\) 的位置,然後將這個位置以前的數加到 \(v\) 裡,然後乘上 \(B_i\) ,然後用同樣的方法處理 \([i + 1,r]\) 。如果區間中沒有一個位置使得 \(B_i \geqslant 2\) ,則把剩下的數加到 \(v\) 中去,最後輸出。
code
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int n;
int a[100005];
int b[100005];
struct node {
LL a;
int b;
int l, r;
node(LL aa, int bb, int c, int d)
: a(aa)
, b(bb)
, l(c)
, r(d) {}
node()
: a(0)
, b(0)
, l(0)
, r(0) {}
};
class xds {
public:
vector<node> tree;
int cnt = 0;
int build(int l, int r) {
int ind = ++cnt;
if (l == r) {
tree[ind] = node(a[l], b[l], 0, 0);
return ind;
}
int mid = (l + r) / 2;
int ll = build(l, mid);
int rr = build(mid + 1, r);
tree[ind] = node(tree[ll].a + tree[rr].a,
max(tree[ll].b, tree[rr].b),
ll,
rr);
return ind;
}
void modify(
int ind, int l, int r, int x, int a, int b) {
if (l == r) {
tree[ind].a = a;
tree[ind].b = b;
return;
}
int mid = (l + r) / 2;
if (x <= mid) modify(tree[ind].l, l, mid, x, a, b);
else modify(tree[ind].r, mid + 1, r, x, a, b);
int ll = tree[ind].l, rr = tree[ind].r;
tree[ind] = node(tree[ll].a + tree[rr].a,
max(tree[ll].b, tree[rr].b),
ll,
rr);
}
node query(int ind, int l, int r, int x, int y) {
if (x <= l && r <= y) {
return tree[ind];
}
int mid = (l + r) / 2;
node ans1, ans2;
if (x <= mid)
ans1 = query(tree[ind].l, l, mid, x, y);
if (mid < y)
ans2 = query(tree[ind].r, mid + 1, r, x, y);
return node(
ans1.a + ans2.a, max(ans1.b, ans2.b), 0, 0);
}
int find(int ind, int l, int r, int x, int y) {
if (l > y || r < x) {
return 0;
}
if (x <= l && r <= y && tree[ind].b < 2) {
return 0;
}
if (l == r) {
return l;
}
int mid = (l + r) / 2;
int ret = find(tree[ind].l, l, mid, x, y);
if (ret == 0) {
ret = find(tree[ind].r, mid + 1, r, x, y);
}
return ret;
}
} t;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
for (int i = 1; i <= n; i++) {
scanf("%d", b + i);
}
t.tree.resize(2 * n + 5);
int root = t.build(1, n);
int q, x, y, op;
scanf("%d", &q);
while (q--) {
scanf("%d%d%d", &op, &x, &y);
if (op == 1) {
a[x] = y;
t.modify(root, 1, n, x, a[x], b[x]);
} else if (op == 2) {
b[x] = y;
t.modify(root, 1, n, x, a[x], b[x]);
} else {
LL ans = a[x];
x++;
while (x <= y) {
int pos = t.find(root, 1, n, x, y);
if (pos == 0) {
ans += t.query(root, 1, n, x, y).a;
break;
} else {
node v =
t.query(root, 1, n, x, pos - 1);
ans += v.a;
ans = max(ans + a[pos], ans * b[pos]);
x = pos + 1;
}
}
printf("%lld\n", ans);
}
}
return 0;
}