A. 369
在 \(A\) 和 \(B\) 之间可以插一个数构成等差数列当且仅当 \(A+B\) 是偶数
特例:\(A=B\),此时答案是 \(1\)
代码实现
a, b = map(int, input().split())
if a == b: print(1)
elif (a+b)%2 == 0: print(3)
else: print(2)
B. Piano 3
把左手和右手分别放在对应的最左侧然后按顺序模拟即可
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
int ans = 0;
int l = -1, r = -1;
rep(i, n) {
int a; char s;
cin >> a >> s;
if (s == 'L') {
if (l != -1) ans += abs(l-a);
l = a;
}
else {
if (r != -1) ans += abs(r-a);
r = a;
}
}
cout << ans << '\n';
return 0;
}
C. Count Arithmetic Subarrays
双指针
代码实现
#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> a(n);
rep(i, n) cin >> a[i];
ll ans = 0;
int r = 0;
rep(l, n) {
while (r < n) {
if (r > l+1 and a[r]-a[r-1] != a[r-1]-a[r-2]) break;
++r;
}
ans += r-l;
}
cout << ans << '\n';
return 0;
}
D. Bonus EXP
简单dp
记 dp[i][j]
表示到第 \(i\) 只怪兽为止已经打的怪兽的个数 \(\% ~2 = j\) 时能获得的最大经验值
时间复杂度为 \(\mathcal{O}(N)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
inline void chmax(ll& x, ll y) { if (x < y) x = y; }
int main() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
const ll INF = 1e18;
vector dp(n+1, vector<ll>(2, -INF));
dp[0][0] = 0;
rep(i, n) {
int ni = i+1;
rep(j, 2) {
{ // o
int nj = j^1;
int X = j%2 ? a[i]*2 : a[i];
chmax(dp[ni][nj], dp[i][j]+X);
}
{ // x
int nj = j;
chmax(dp[ni][nj], dp[i][j]);
}
}
}
ll ans = max(dp[n][0], dp[n][1]);
cout << ans << '\n';
return 0;
}
E. Sightseeing Tour
注意到 \(k \leqslant 5\) 这个条件,这道题就做完了
只需枚举这 \(k\) 条边不同的顺序和每条边的方向
\(n \leqslant 400\) 暗示我们可以跑 \(\operatorname{floyd}\)
那么复杂度就是 \(\mathcal{O}(n^3 + qk!2^k)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using Edge = tuple<int, int, int>;
inline void chmin(ll& a, ll b) { if (a > b) a = b; }
int main() {
int n, m;
cin >> n >> m;
vector<Edge> es;
const ll INF = 1e18;
vector dist(n, vector<ll>(n, INF));
rep(i, n) dist[i][i] = 0;
rep(i, m) {
int a, b, c;
cin >> a >> b >> c;
--a; --b;
es.emplace_back(a, b, c);
chmin(dist[a][b], c);
chmin(dist[b][a], c);
}
rep(k, n)rep(i, n)rep(j, n) chmin(dist[i][j], dist[i][k]+dist[k][j]);
int q;
cin >> q;
rep(qi, q) {
int k;
cin >> k;
vector<int> ei(k);
rep(i, k) cin >> ei[i], ei[i]--;
ll ans = INF;
do {
rep(s, 1<<k) {
ll now = 0;
int v = 0;
rep(i, k) {
auto [a, b, c] = es[ei[i]];
if (s>>i&1) swap(a, b);
now += dist[v][a];
now += c;
v = b;
}
now += dist[v][n-1];
chmin(ans, now);
}
} while (next_permutation(ei.begin(), ei.end()));
cout << ans << '\n';
}
return 0;
}
F. Gather Coins
这题本质上就是求二维上升子序列
可以用扫描线和线段树加速
代码实现
#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 P = pair<int, int>;
P op(P a, P b) { return max(a, b); }
P e() { return P(0, -1); }
int main() {
int h, w, n;
cin >> h >> w >> n;
vector<P> coins;
rep(i, n) {
int r, c;
cin >> r >> c;
coins.emplace_back(r, c);
}
ranges::sort(coins);
const int MX = 200005;
segtree<P, op, e> t(MX);
vector<int> pre(n);
rep(i, n) {
int y = coins[i].second;
auto [lis, idx] = t.prod(0, y+1);
pre[i] = idx;
t.set(y, P(lis+1, i));
}
string ans;
int r = h, c = w;
auto mv = [&](P p) {
auto [nr, nc] = p;
while (r > nr) r--, ans += 'D';
while (c > nc) c--, ans += 'R';
};
auto [lis, i] = t.all_prod();
while (i != -1) {
mv(coins[i]);
i = pre[i];
}
mv(P(1, 1));
ranges::reverse(ans);
cout << lis << '\n';
cout << ans << '\n';
return 0;
}
G. As far as possible
可以发现答案就是由点 \(1\) 和指定的 \(k\) 个点构成的树形图上每条边的边权 \(\times 2\) 的总和
青木为了让答案最大,应该尽可能地选距离点 \(1\) 更远的叶子节点,等选完叶子节点以后,剩下的答案保持不变
那么怎么来求答案呢?
- 树形dp+multiset做dsu on tree
我们可以对树上每个点维护一个可重集,对于 \(v \to u\) 这条边,边权为 \(w\),取出点 \(u\) 的可重集中的最大值 \(x\),同时将这个最大值在点 \(u\) 的可重集中删去,然后将 \(x+w\) 加入点 \(v\) 的可重集中
具体分析可以参考 \(\operatorname{t\color{\red}{iger2005}}\) 的题解
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
struct Edge {
int to, cost;
Edge() {}
Edge(int to, int cost): to(to), cost(cost) {}
};
int main() {
int n;
cin >> n;
vector<vector<Edge>> g(n);
rep(i, n-1) {
int a, b, c;
cin >> a >> b >> c;
--a; --b;
g[a].emplace_back(b, c);
g[b].emplace_back(a, c);
}
auto dfs = [&](auto& dfs, int v, int p=-1) -> multiset<ll> {
multiset<ll> f;
for (auto [u, w] : g[v]) {
if (u == p) continue;
auto nf = dfs(dfs, u, v);
ll x = *nf.rbegin(); nf.erase(prev(nf.end()));
nf.insert(x+w);
if (f.size() < nf.size()) swap(f, nf);
f.merge(nf);
}
f.insert(0);
return f;
};
auto f = dfs(dfs, 0);
vector<ll> as;
for (ll x : f) as.push_back(x);
ranges::reverse(as);
ll ans = 0;
for (ll a : as) {
cerr << a << '\n';
ans += a*2;
cout << ans << '\n';
}
return 0;
}