CF343E Tutorial
Aleph_Drawer · · 题解
首先,看到要求多个最小割,以及较小的数据范围,不难想到最小割树。
那么我们先把最小割树求出来吧。接下来我们来考虑怎么构造使得值最大。具体的,我们现在要构造一个排列
发现这个值的上界是
首先,我们从大到小考虑每一条边。显然的,如果我们想要加入这条边的贡献,我们可以经过比这条边权值大的边,但是不能经过比这条边权值小的边。
发现对于边
此时,我们可以做并查集:每次做完一条边之后,我们就把这两个点合并,每次通过并查集和链表找到
不妨给每一个点多开两个变量,一个作为链表的 next 指针,另一个,则指向这个链表的尾部(只用对并查集中的根节点维护这个),此时合并两个连通块的操作就变成了合并两个链表,由于上述两个指针的存在,合并可以做到
假设下图是我们两个要合并的集合
此时,我们便可以很好的理解指向链表尾部是什么意思了。首先,我们通过并查集找到了
通过图示的红色的边找到
此时两个链表就已经拼在一起了。我们还要对
通过红色边找到
这样就完成了合并操作。
所以这部分的时间复杂度是
最小割树由于怕代码长度限制就不放上来了,这里放主函数和合并部分。
int fath[N];
struct link {
int nxt, ed;
}p[N];
int getf(int x) {
return x == fath[x] ? x : (fath[x] = getf(fath[x]));
}
void merge(int x, int y) {
x = getf(x), y = getf(y);
if(x == y) return;
// x -> y
p[p[x].ed].nxt = y;
p[x].ed = p[y].ed; // 这里即为上面一大堆描述的过程
fath[y] = x;
return;
}
int main() {
scanf("%d%d", &n, &m);
// namespace GHT 即为 Gomory-Hu Tree,最小割树,此处是预处理,下面的 build(int,int) 是构建
for(int i = 1; i <= n; i++)
GHT::s[i] = i;
for(int i = 1; i <= m; i++)
scanf("%d%d%d", &fr[i], &to[i], &val[i]);
GHT::build(1, n);
int tot = 0;
// struct GHT_graph{}TG 即为最小割树所在的图
for(int i = 1; i <= TG.e_sz; i++)
tot += TG.e[i].w;
printf("%d\n", tot);
stable_sort(TG.e + 1, TG.e + TG.e_sz + 1);
// 已经定义:friend bool operator < (edge x, edge y) {return x.w > y.w}
for(int i = 1; i <= n; i++)
fath[i] = i, p[i].nxt = i, p[i].ed = i;
for(int i = 1; i <= TG.e_sz; i++)
merge(TG.e[i].u, TG.e[i].v);
int pr = getf(1);
printf("%d", pr);
while(p[pr].nxt != pr) {
pr = p[pr].nxt;
printf(" %d", pr);
}
printf("\n");
return 0;
}