传送门HHKB Programming Contest 2022 Winter(AtCoder Beginner Contest 282)
D - Make Bipartite 2
Tutorial
考虑三种情况:
- 只有一个联通分量,且该联通分量本身为一个二分图:
此时染 c 1 c_1 c1 个黑点, c 2 c_2 c2 个白点,,则答案即为 ( c 1 ∗ c 2 − m ) (c_1*c_2-m) (c1∗c2−m) ,表示所有满足二分图的边除去已有的边 - 有多个连通分量,且各联通分量都为二分图:
对其中一个联通分量染色得到所有可能的边 r e s 1 = c 1 ∗ c 2 res_1=c_1*c_2 res1=c1∗c2,当前联通分量可以和其他联通分量之间连多少条边 r e s 2 = ( c 1 + c 2 ) ∗ ( n − ( c 1 + c 2 ) ) res_2=(c_1+c_2)*(n-(c_1+c_2)) res2=(c1+c2)∗(n−(c1+c2))
在 r e s 2 res_2 res2 的求解过程中,各连通分量会对同一条边计算两次,所以我们最终的答案为 a n s = r e s 1 + r e s 2 2 − m ans=res_1+\frac{res_2}2-m ans=res1+2res2−m
注意如果任意联通分量中存在非二分图,那么不论怎么加边都无法保证整个图内没有长度为奇数的回路,所以答案是0
Solution
#include <bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pii pair<int, int>
#define inf 0x3f3f3f3f
const int N = 2e5 + 7;
using namespace std;
vector<int> e[N];
int g[N];
int c1, c2;
bool dfs(int u, int col) {
g[u] = col;
c1 += (col == 1);
c2 += (col == 2);
for (auto i: e[u]) {
if (g[i] == g[u]) return 0;
if (!g[i] && !dfs(i, 3 - col)) return 0;
}
return 1;
}
signed main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
e[a].push_back(b);
e[b].push_back(a);
}
int res1 = 0, res2 = 0;
for (int i = 1; i <= n; i++)
if (!g[i]) {
c1 = c2 = 0;
if (!dfs(i, 1)) {
cout << "0\n";
return 0;
}
res1 += c1 * c2;
res2 += (c1 + c2) * (n - c1 - c2);
}
cout << res1 + res2 / 2 - m << '\n';
}
E - Choose Two and Eat One
Tutorial
相当于给定一棵树,每次将叶子节点去掉,同时获得叶子与父亲的边权值。
跑一遍最大生成树即可。
Solution
#include <bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pii pair<int, int>
#define inf 0x3f3f3f3f
const int N = 500 + 7;
using namespace std;
struct Node {
int a, b, w;
} edge[N * N];
int n, m;
int a[N], p[N];
int qpow(int x, int y) {
int res = 1;
while (y) {
if (y & 1) res = res * x % m;
x = x * x % m;
y >>= 1;
}
return res;
}
int calc(int x, int y) {
return (qpow(x, y) + qpow(y, x)) % m;
}
int find(int x) {
if (p[x] == x) return x;
return p[x] = find(p[x]);
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
int t = 0;
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++) edge[++t] = {i, j, calc(a[i], a[j])};
for (int i = 1; i <= n; i++) p[i] = i;
sort(edge + 1, edge + t + 1, [](const Node &x, const Node &y) { return x.w > y.w; });
int ans = 0;
for (int i = 1; i <= t; i++) {
int x = find(edge[i].a), y = find(edge[i].b);
if (x != y) p[x] = y, ans += edge[i].w;
}
cout << ans;
}