C. Cantrip
每个 \(k\) 的答案就是第 \(k\) 个 x 的位置(若不存在则为 \(n\))
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
vector<int> ans;
rep(i, n) {
if (s[i] == 'x') ans.push_back(i+1);
}
while (ans.size() < n) ans.push_back(n);
rep(i, n) cout << ans[i] << '\n';
return 0;
}
D. The Big Two
把每次决赛对 \((A_m ,B_m)\) 看成无向边,题目等价于统计大小为 \(2\) 的顶点覆盖对数(每条边至少有一个端点在这两个顶点中)。
任取第一条边 \((s,t)\),任一合法对必包含 \(s\) 或包含 \(t\),因此只需枚举
\(v∈{s,t}\) 。
固定 \(v\) 后,删去所有与 \(v\) 相连的边,剩下的每条边都必须被另一个顶点 \(u\) 覆盖,故 \(u\) 必须出现在所有剩余边中(即在这些边中出现次数等于剩余边数)。统计满足条件的 \(u\) 即得与 \(v\) 配对的数量。
对 \(s,t\) 分别计数并合并(注意避免重复计数 \((s,t)\) 本身)。复杂度 \(\mathcal{O}(m+n)\) 。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int main() {
int n, m;
cin >> n >> m;
vector<P> es;
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
es.emplace_back(a, b);
}
int ans = 0;
auto [s, t] = es[0];
for (int v : {s, t}) {
int num = 0;
vector<int> cnt(n);
cnt[v] = -1;
if (v == t) cnt[s] = -1;
for (auto [a, b] : es) {
if (a == v or b == v) continue;
num++;
cnt[a]++; cnt[b]++;
}
rep(i, n) {
if (cnt[i] == num) ans++;
}
}
cout << ans << '\n';
return 0;
}
E. Pro Exam Eligibility
二分答案
假设最大胜率为 \(x\)
\(x \leqslant \frac{\text{和}}{\text{长度}} \Leftrightarrow \text{和}-\text{长度} \times x \geqslant 0\)
这里的和指的是区间里 o 的个数,可以用前缀和来维护(\(s_i\) 为 x 时权值为 \(1\),否则权值为 \(0\))
不妨记这个前缀和为 \(a\),和就是 \(a_r - a_{l-1}\),为了让和最大,可以取最小的 \(a_{l-1}\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int main() {
int n, k;
string s;
cin >> n >> k >> s;
auto judge = [&](double x) {
vector<double> a(n+1);
rep(i, n) {
double w = -x;
if (s[i] == 'o') w += 1;
a[i+1] = a[i]+w;
}
int p = 0, o = 0;
double minA = 1e18;
rep(r, n) {
if (s[r] == 'o') o++;
while (o >= k) {
minA = min(minA, a[p]);
if (s[p] == 'o') o--;
p++;
}
// a[r+1]-a[l] (0 <= l < p)
if (a[r+1]-minA >= 0) return true;
}
return false;
};
double ac = 0, wa = 1;
rep(ti, 25) {
double wj = (ac+wa)/2;
if (judge(wj)) ac = wj; else wa = wj;
}
printf("%.10f\n", ac);
return 0;
}
F. GCD Maximum Spanning Tree
最大生成树可以用 \(\text{Kruskal}\):按边权从大到小加入不产生环的边并累加权值。
注意若边权为 \(w\),那么只有数值都被 \(w\) 整除的顶点对才可能有 \(\gcd(A_i,A_j) \geqslant w\)。因此枚举 \(w\) 从大到小,考虑所有值 \(i=w,2w,3w, \cdots\) 中存在于输入的数,把这些值用并查集合并:遇到两个不在同一分量的存在值就合并并加上权重 \(w\)(相当于在这组中选取若干条权为 \(w\) 的边连接分量)。
这样等价于对所有边按权降序的 \(\text{Kruskal}\),最终得到最大生成树和。
代码实现
#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 ll = long long;
int main() {
int n;
cin >> n;
int m = 1e6 ;
vector<bool> exist(m+1);
rep(i, n) {
int a;
cin >> a;
exist[a] = true;
}
ll ans = 0;
dsu uf(m+1);
for (int w = m; w >= 1; --w) {
int p = -1;
for (int i = w; i <= m; i += w) {
if (exist[i]) {
if (p != -1 and !uf.same(p, i)) {
uf.merge(p, i);
ans += w;
}
p = i;
}
}
}
cout << ans << '\n';
return 0;
}
浙公网安备 33010602011771号