A. Cut
模拟
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n);
rep(i, n) cin >> a[i];
rotate(a.begin(), a.begin()+(n-k), a.end());
rep(i, n) cout << a[i] << ' ';
return 0;
}
B. Decrease 2 max elements
模拟
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
int ans = 0;
while (1) {
ranges::sort(a, greater<>());
if (a[1] <= 0) break;
a[0]--; a[1]--;
ans++;
}
cout << ans << '\n';
return 0;
}
C. Triple Attack
周期性
把 {1, 1, 3} 看成是一个组合技,用这 \(3\) 次攻击可以让敌人掉 \(5\) 点血
对每个敌人先尽可能地用组合技,如果还有剩余血量,就暴力模拟即可
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
vector<int> h(n);
rep(i, n) cin >> h[i];
ll t = 0;
rep(i, n) {
int x = h[i]/5;
t += x*3;
h[i] -= x*5;
while (h[i] > 0) {
t++;
if (t%3 == 0) h[i] -= 3;
else h[i]--;
}
}
cout << t << '\n';
return 0;
}
D. Minimum Steiner Tree
显然应该从叶子节点开始删,可以考虑以某个关键点跑dfs
一个点被保留当且仅当以它为根的子树中包含关键点
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector<vector<int>> to(n);
rep(i, n-1) {
int a, b;
cin >> a >> b;
--a; --b;
to[a].push_back(b);
to[b].push_back(a);
}
vector<int> vs(k);
rep(i, k) cin >> vs[i], vs[i]--;
vector<bool> selected(n);
rep(i, k) selected[vs[i]] = true;
vector<int> num(n);
auto dfs = [&](auto& f, int v, int p=-1) -> void {
if (selected[v]) num[v]++;
for (int u : to[v]) {
if (u == p) continue;
f(f, u, v);
num[v] += num[u];
}
};
dfs(dfs, vs[0]);
int ans = 0;
rep(i, n) if (num[i] > 0) ans++;
cout << ans << '\n';
return 0;
}
E. Train Delay
注意到,\(
T_i + X_i \leqslant S_j + X_j \Leftrightarrow T_i + X_i - S_j \leqslant X_j
\)
于是,可以得到 \(X_j = \max(T_i + X_i) - S_j\)
可以按 \(S_j\) 递增的顺序来求出所有的 \(X_i\),暴力模拟的时间复杂度为 \(\mathcal{O}(M^2)\)
下面考虑优化:
对每个站点维护一个二元组序列 \(\{(T_i, T_i+X_i)\}\) 的信息
对于 \(\max(T_i+X_i)\),可以开一个数组 maxT
来维护,\(maxT[a]\) 就表示到站时间在当前火车的发车时间之前且目的地在站点 \(a\) 处的 \(\max(T_i + X_i)\)
可以考虑用小根堆来维护每个站点的二元组序列,这样就能保证 \(T_i\) 单调递增了
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
using PQ = priority_queue<P, vector<P>, greater<P>>;
struct Train {
int a, b, s, t, i;
Train(int a, int b, int s, int t, int i): a(a), b(b), s(s), t(t), i(i) {}
bool operator<(const Train& o) const {
return s < o.s;
}
};
int main() {
int n, m, x1;
cin >> n >> m >> x1;
vector<Train> trains;
rep(i, m) {
int a, b, s, t;
cin >> a >> b >> s >> t;
--a; --b;
trains.emplace_back(a, b, s, t, i);
}
sort(trains.begin(), trains.end());
vector<int> x(m);
vector<int> maxT(n);
vector<PQ> q(n);
for (auto [a, b, s, t, i] : trains) {
if (i == 0) x[i] = x1;
else {
while (q[a].size()) {
auto [nt, val] = q[a].top();
if (nt > s) break;
q[a].pop();
maxT[a] = max(maxT[a], val);
}
x[i] = max(0, maxT[a]-s);
}
q[b].emplace(t, t+x[i]);
}
rep(i, m) if (i) cout << x[i] << ' ';
return 0;
}
F. Dividing Game
对每个数求一遍 \(sg\) 函数,打表可以发现 \(sg(x)\) 就是 \(x\) 的可重复质因子集合的大小,然后直接套 \(sg\) 定理即可
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
const int M = 100005;
vector<int> g(M);
for (int i = 1; i < M; ++i) {
int now = 0;
int x = i;
for (int d = 2; d*d <= x; ++d) {
if (x%d != 0) continue;
while (x%d == 0) now++, x /= d;
}
if (x != 1) now++;
g[i] = now;
}
int n;
cin >> n;
int x = 0;
rep(i, n) {
int a; cin >> a;
x ^= g[a];
}
if (x == 0) puts("Bruno");
else puts("Anna");
return 0;
}
G. Add and Multiply Queries
黑体字是突破口,注意到询问3的答案不超过 \(1e18\) 就很简单了
如果区间 \([l, r]\) 中的 \(B_i \neq 0\) 的个数超过 \(60\) 个,那么执行乘法运算的话显然会超过 \(1e18\),所以我们可以大胆断定区间中 \(B_i \neq 0\) 的位置不超过 \(60\) 个
对于 \(B_i = 1\) 的连续区间只需执行加法即可
考虑维护满足以下需求的数据结构:
- 求出下一个满足 \(B_i \geqslant 2\) 的位置 \(i\) \(\to\)
std::set
- 区间中 \(A\) 的累加和 \(\to\) 树状数组
代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
int n;
cin >> n;
vector<int> a(n), b(n);
rep(i, n) cin >> a[i];
rep(i, n) cin >> b[i];
fenwick_tree<ll> d(n);
rep(i, n) d.add(i, a[i]);
set<int> st;
rep(i, n) if (b[i] > 1) st.insert(i);
st.insert(n);
int q;
cin >> q;
rep(qi, q) {
int type;
cin >> type;
if (type == 3) {
int l, r;
cin >> l >> r;
--l;
ll v = 0;
while (l < r) {
int i = *st.lower_bound(l);
i = min(i, r);
v += d.sum(l, i);
if (i == r) break;
v = max(v+a[i], v*b[i]);
l = i+1;
}
cout << v << '\n';
}
else {
int i, x;
cin >> i >> x;
--i;
if (type == 1) {
d.add(i, x-a[i]);
a[i] = x;
}
else {
b[i] = x;
if (b[i] > 1) st.insert(i); else st.erase(i);
}
}
}
return 0;
}