C. New Skill Acquired
多源bfs
代码实现
#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<vector<int>> to(n);
vector<int> got;
rep(i, n) {
int a, b;
cin >> a >> b;
if (a == 0) {
got.push_back(i);
}
else {
--a; --b;
to[a].push_back(i);
to[b].push_back(i);
}
}
vector<bool> used(n);
queue<int> q;
for (int v : got) {
used[v] = true;
q.push(v);
}
while (q.size()) {
int v = q.front(); q.pop();
for (int u : to[v]) {
if (used[u]) continue;
used[u] = true;
q.push(u);
}
}
int ans = 0;
rep(i, n) if (used[i]) ans++;
cout << ans << '\n';
return 0;
}
D. 2x2 Erasing 2
容易发现答案不超过 \(9\)
那么最多有 \(C(49, 9)\) 种方案,属于极限逼近能过的范围,然后简单剪一下枝就过了
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void solve() {
int h, w;
cin >> h >> w;
vector<string> s(h);
rep(i, h) cin >> s[i];
int ans = 9;
auto f = [&](auto& f, int now) -> void {
if (now >= ans) return;
rep(i, h-1)rep(j, w-1) {
int cnt = 0;
rep(di, 2)rep(dj, 2) if (s[i+di][j+dj] == '#') cnt++;
if (cnt == 4) {
rep(dj, 2) {
s[i+1][j+dj] = '.';
f(f, now+1);
s[i+1][j+dj] = '#';
}
return;
}
}
ans = min(ans, now);
};
f(f, 0);
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
E. Cut in Half
用大根堆来维护二元组 (值,数量),批量处理相同长度的木棍的切割操作
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void solve() {
int n, k, x;
cin >> n >> k >> x;
using P = pair<double, int>;
priority_queue<P> q;
rep(i, n) {
int a;
cin >> a;
q.emplace(a, 1);
}
while (k) {
auto [l, c] = q.top(); q.pop();
if (k < c) {
q.emplace(l, c-k);
c = k;
}
k -= c;
q.emplace(l/2, c*2);
}
while (1) {
auto [l, c] = q.top(); q.pop();
x -= c;
if (x <= 0) {
printf("%.10f\n", l);
return;
}
}
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
F. Adding Chords
性质:
区间 \(I\) 和区间 \(J\) 不相交 \(\Leftrightarrow\) \(I\) 包含 \(J\) 的偶数个交点
考虑一个初值都为 \(0\) 的序列 \(X=(X_1, X_2, \cdots, X_N)\)
每次对点 \(l\) 和点 \(r\) 连一条线段时,就将区间 \([X_l, X_{l+1}, \cdots, X_{r-1}]\) 上每个值加 \(1\)
然后利用上面的性质,只需保证区间 \([l, r)\) 的区间和以及其中的最小值为 \(0\) 就能添加一条线段 \((l, r)\)
可以用线段树来实现
实际上还有一种更简单的做法,就是每次对区间加一个哈希值,这样就不需要再判定区间最小值是否为 \(0\) 了
可以用树状数组来实现
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ull = unsigned long long;
int main() {
random_device seed_gen;
mt19937_64 rnd(seed_gen());
int n, q;
cin >> n >> q;
fenwick_tree<ull> d(n+1);
rep(qi, q) {
int l, r;
cin >> l >> r;
if (d.sum(0, l) == d.sum(0, r)) {
puts("Yes");
ull x = rnd();
d.add(l, x); d.add(r, -x);
}
else puts("No");
}
return 0;
}
G. Set list
最小割
先将歌曲按 \(b\) 值降序排好
定义 dp[i][k][s] 表示考虑前 \(i\) 首歌曲时,已选 \(k\) 首歌且使用 \(s\) 人时的最大活跃度
代码实现
#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& a, ll b) { if (a < b) a = b; }
int main() {
int n, m;
cin >> n >> m;
vector<int> a(n);
vector<pair<int, int>> bc(m);
rep(i, n) cin >> a[i];
rep(i, m) cin >> bc[i].first >> bc[i].second;
ranges::sort(a);
ranges::sort(bc, greater<>());
const int INF = 1001001001;
vector<int> ub(m+1, INF);
rep(i, m+1) {
int sa = 0;
for (int x = 1; x <= n; ++x) {
sa += a[x-1];
ub[i] = min(ub[i], (n-x)*i + sa);
}
}
int mx = n*m+1;
vector dp(m+1, vector<ll>(mx, -1e18));
dp[0][0] = 0;
rep(i, m) {
auto [b, c] = bc[i];
for (int j = m-1; j >= 0; --j) {
rep(k, mx) if (dp[j][k] >= 0) {
int nj = j+1, nk = k+b;
if (ub[nj] < nk) continue;
chmax(dp[nj][nk], dp[j][k]+c);
}
}
}
ll ans = 0;
rep(i, m+1)rep(j, mx) chmax(ans, dp[i][j]);
cout << ans << '\n';
return 0;
}
浙公网安备 33010602011771号