[每日 C] Graph Composition
前言
你说的对, 但是 \(\textrm{div 3 E}\) 应该有 \(\textrm{div 2 C}\) 的水平了吧
思路
转化题意
首先给定两张点数一样的简单无向图
一次操作可以添加或删除 \(F\) 图中的一个点, 求最少的操作次数使得 \(F\) 和 \(G\) 完全相同
问题转化成如何快速判断图中是否有这条边
感觉直接在边集里面二分就可以了啊, 好像很简单的样子, 复杂度 \(\mathcal{O} (m_2 \log n)\)
搞半天看错题了, 是路径
想一下, 本质上是将连通块转化成一样的形式
涉及到了离开一个连通块和加入一个连通块的问题
我们先用 \(\mathcal{O} (n)\) 的时间把目标图中有的连通块加到一起
这之后, 我们考虑把连通块分开
具体的, 我们对于每一条边计数, 判断这条边是否要删去, 然后删了就行
代码
#include <bits/stdc++.h>
#define int long long
const int MAXN = 5e5 + 20;
int n, m1, m2;
struct node { int from, to; };
node e1[MAXN], e2[MAXN];
struct dsu_struct {
int fa[MAXN];
void init() { for (int i = 1; i <= n; i++) fa[i] = i; }
int find(int x) { return fa[x] = (x == fa[x] ? x : find(fa[x])); }
void merge(int u, int v) { fa[find(v)] = find(u); }
} dsu1, dsu2;
signed main()
{
int t; scanf("%lld", &t);
while (t--) {
scanf("%lld %lld %lld", &n, &m1, &m2);
dsu1.init(), dsu2.init();
for (int i = 1; i <= m1; i++) {
int u, v; scanf("%lld %lld", &u, &v);
e1[i] = {u, v};
dsu1.merge(u, v);
}
for (int i = 1; i <= m2; i++) {
int u, v; scanf("%lld %lld", &u, &v);
e2[i] = {u, v};
dsu2.merge(u, v);
}
int ans = 0;
for (int i = 1; i <= n; i++) if (dsu1.find(dsu2.find(i)) != dsu1.find(i)) ans++, dsu1.merge(dsu2.find(i), i), e1[++m1] = {dsu2.find(i), i};
for (int i = 1; i <= m1; i++) {
int u = e1[i].from, v = e1[i].to;
if (dsu2.find(u) != dsu2.find(v)) {ans++;e1[i] = {0, 0};}
}
dsu1.init();
for (int i = 1; i <= m1; i++) {
int u, v; u = e1[i].from, v = e1[i].to;
dsu1.merge(u, v);
}for (int i = 1; i <= n; i++) if (dsu1.find(dsu2.find(i)) != dsu1.find(i)) ans++, dsu1.merge(dsu2.find(i), i);
printf("%lld\n", ans);
}
return 0;
}
总结
补丁大蛇还在发力
对拍是好东西
有了对拍, 可以搞出一些 \(\textrm{corner case}\) 供调试用, 方便打补丁
然后就是加油, 继续每日一练

浙公网安备 33010602011771号