A. Who Ate the Cake?
模拟
代码实现
a, b = map(int, input().split())
if a == b:
print(-1)
else:
print(6-a-b)
B. Piano 2
模拟
代码实现
#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<int> a(n), b(m);
rep(i, n) cin >> a[i];
rep(i, m) cin >> b[i];
vector<P> ps;
rep(i, n) ps.emplace_back(a[i], 0);
rep(i, m) ps.emplace_back(b[i], 1);
sort(ps.begin(), ps.end());
rep(i, n+m-1) {
if (ps[i].second == 0 and ps[i+1].second == 0) {
puts("Yes");
return 0;
}
}
puts("No");
return 0;
}
C. Bingo 2
模拟
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, t;
cin >> n >> t;
vector<int> row(n), col(n);
int dia1 = 0, dia2 = 0;
rep(ti, t) {
int a;
cin >> a;
--a;
int i = a/n, j = a%n;
auto add = [&](int& x) {
x++;
if (x == n) {
cout << ti+1 << '\n';
exit(0);
}
};
add(row[i]);
add(col[j]);
if (i == j) add(dia1);
if (i+j == n-1) add(dia2);
}
puts("-1");
return 0;
}
D. Intersecting Intervals
可以用总方案数容斥掉不相交的区间的个数
什么时候两区间会不相交?一个区间的右端点小于另一个区间的左端点
那么我们可以像B题那样对区间左右两端点分别标记为 \(0\) 和 \(1\),然后放在一起排序,接着遍历所有二元组 \((x, t)\),当 \(t=0\) 时,将答案减去前面右端点的个数,当 \(t=1\) 时,将右端点个数 +1
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
using ll = long long;
int main() {
int n;
cin >> n;
vector<P> ps;
rep(i, n) {
int l, r;
cin >> l >> r;
ps.emplace_back(l, 0);
ps.emplace_back(r, 1);
}
sort(ps.begin(), ps.end());
ll ans = (ll)n*(n-1)/2;
int rs = 0;
for (auto [x, t] : ps) {
if (t == 0) ans -= rs;
else rs++;
}
cout << ans << '\n';
return 0;
}
E. Guess the Sum
为了方便处理,不妨将 \(R\) 加上 \(1\)
记 \(S_i = \sum\limits_{j = 0}^{i-1} a_j\)
注意到 \(S_R - S_L = (-1)\times S_R + 1 \times S_L\),同样对于 \(S_r - S_l\) 也可以表示成 \((-1)\times S_l + 1 \times S_r\)
假设从当前点向右边连边为正贡献,向左连边为负贡献,分别用 1
和 -1
来标记
那么 \(S_R - S_L\) 就可以由从 \(L\) 点出发经过若干条边走到 \(R\) 点的最短路得到,关于如何求最短路,可以从当前点向左右两边进行扩展,也就是 \(v - 2^i\) 以及 \(v+2^i\),直到扩展到 \(v\) 点在二进制下末尾第一个 \(1\) 的位置
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
using ll = long long;
int main() {
int n, l, r;
cin >> n >> l >> r;
int n2 = 1<<n;
r++;
const int INF = 1001001001;
vector<int> dist(n2+1, INF), pre(n2+1, -1);
queue<int> q;
dist[l] = 0; q.push(l);
while (q.size()) {
int v = q.front(); q.pop();
auto push = [&](int to) {
if (to < 0 or to > n2) return;
if (dist[to] != INF) return;
dist[to] = dist[v]+1;
pre[to] = v;
q.push(to);
};
rep(i, n+1) {
push(v-(1<<i));
push(v+(1<<i));
if (v>>i&1) break;
}
}
int ans = 0;
auto query = [&](int s, int t) {
int sign = 1;
if (s > t) swap(s, t), sign = -1;
{
int i = 0, j = s, w = t-s;
while (w%2 == 0) j >>= 1, i++, w >>= 1;
cout << "? " << i << ' ' << j << '\n';
}
int x;
cin >> x;
ans = (ans + x*sign + 100)%100;
};
while (r != l) {
query(pre[r], r);
r = pre[r];
}
cout << "! " << ans << '\n';
return 0;
}
F. MST Query
考虑图 \(G\) 的子图,由 \(G\) 上边权大小不超过 \(k\) 的边生成的子图 \(G_k (0 \leqslant k \leqslant 10)\)。记图 \(H\) 中的连通分量的个数为 \(c(H)\),那么图 \(G\) 上的最小生成树上的所有边的权值总和等价于 \(\sum\limits_{k=0}^9 (c(G_k)-1)\) 。
证明:对于图 \(G\) 的最小生成树上的边,边权不超过 \(k\) 的边有 \(N-c(G_k)\)。所以,最小生成树上边权恰好为 \(k\) 的边数就是 \((N-c(G_k)) - (N-c(G_{k-1})) = c(G_{k-1}) - c(G_k)\)。于是,最小生成树上的边权和就是 \(\sum\limits_{k=1}^{10} k \cdot (c(G_{k-1}) - c(G_k)) = \sum\limits_{k=0}^9 c(G_k) - 10c(G_{10}) = \sum\limits_{k=0}^9 (c(G_k)-1)\),其中 \(G_{10}\) 就是图 \(G\),所以连通分量的个数为 \(1\)
代码实现
#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;
int main() {
int n, q;
cin >> n >> q;
int m = 10;
int ans = (n-1)*m;
vector<dsu> uf(m, dsu(n));
rep(qi, n-1+q) {
int a, b, c;
cin >> a >> b >> c;
--a; --b;
for (int i = c; i < m; ++i) {
if (uf[i].same(a, b)) continue;
ans--;
uf[i].merge(a, b);
}
if (qi < n-1) continue;
cout << ans << '\n';
}
return 0;
}