BZOJ2229: [Zjoi2011]最小割(最小割树)
最小割树
算法
初始时把所有点放在一个集合
从中任选两个点出来跑原图中的最小割
然后按照 \(s\) 集合与 \(t\) 集合的归属把当前集合划分成两个集合,递归处理
这样一共跑了 \(n − 1\) 次最小割
可以证明图中任意一对点之间的最小割的数值都包含在这 \(n − 1\) 个数值当中
把每次求出的最小割看成是两个点之间的边,可以建出一棵树
定理1
任意三点之间的最小割一定是两个相等的较小值和一个较大值
证明
设任意三点 \(a, b, c\) 之间的最小割分别为 \(mincut(a, b), mincut(a, c), mincut(b, c)\)
设 \(mincut(a, b)\) 是这三者中的最小值
那么在做 \(a − b\) 割时,\(c\) 一定属于其中的某一个集合,设其与 \(a\) 同属于一个集合
那么就有 \(mincut(b, c) \le mincut(a, b)\)
又因为 \(mincut(a, b) \le mincut(b, c)\),所以两者相等
定理2
图中至多只有 \(n − 1\) 种不同的最小割
证明
编了一个构造的证明
首先根据定理 \(1\) ,可以转化成如下问题:
\(n\) 个点的无向完全图,要给每一条边染色,要求每个三元环上有两条边同色,求最多可以染多少种颜色
考虑归纳法
假设现在已经有一条长度大于 \(1\) 的链 \(a\) 到 \(b\) 上的边的颜色互不相同
考虑染 \((a,b)\) 这条边
如果链长 \(=2\),那么 \((a,b)\) 只能选择链上某条边的颜色
如果链长 \(>2\),假设两个端点在链上的不是 \((a,b)\) 的边的颜色都是链上某条边的颜色
那么取出其中两条 \((a,c)\) 和 \((c,b)\),\((a,b)\) 只能选择两个中其中一条边的颜色,即链上某条边的颜色
所以可以得到,颜色互不相同的边不能形成环,所以最优的显然是树,即 \(n-1\) 中颜色
Sol
至此问题已经完美解决
建出最小割树,任意两个点的最小割就是路径边权最小值
直接暴力也可以
复杂度貌似 \(\Theta(n^3m)\) \(???\)
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn(505);
const int inf(1e9);
int first[maxn], n, m, cnt, lev[maxn], vis[maxn], s, t, id[maxn], tmp[maxn], cur[maxn], mincut[maxn][maxn], record[maxn << 4];
queue <int> q;
struct Edge {
int to, next, w;
} edge[maxn << 4];
inline void Add(int u, int v, int w) {
edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
record[cnt - 1] = record[cnt - 2] = w;
}
inline int Bfs() {
memset(lev, 0, sizeof(lev)), lev[s] = 1, q.push(s);
register int u, e, v;
while (!q.empty()) {
for (u = q.front(), q.pop(), e = first[u]; ~e; e = edge[e].next)
if (edge[e].w && !lev[v = edge[e].to]) lev[v] = lev[u] + 1, q.push(v);
}
return lev[t];
}
int Dfs(int u, int maxf) {
if (u == t) return maxf;
register int ret = 0, &e = cur[u], f, v;
for (; ~e; e = edge[e].next)
if (edge[e].w && lev[v = edge[e].to] == lev[u] + 1){
f = Dfs(v, min(edge[e].w, maxf - ret));
ret += f, edge[e].w -= f, edge[e ^ 1].w += f;
if (ret == maxf) return ret;
}
if (!ret) lev[u] = 0;
return ret;
}
inline int Dinic() {
register int ret = 0, i;
for (i = 0; i < cnt; ++i) edge[i].w = record[i];
while (Bfs()) memcpy(cur, first, sizeof(cur)), ret += Dfs(s, inf);
return ret;
}
void Mark(int u) {
if (vis[u]) return;
register int e;
for (vis[u] = 1, e = first[u]; ~e; e = edge[e].next) if (edge[e].w) Mark(edge[e].to);
}
inline void Solve(int l, int r) {
if (l >= r) return;
memset(vis, 0, sizeof(vis)), s = id[l], t = id[r];
register int i, j, mid, d = Dinic();
for (Mark(s), j = 0, i = l; i <= r; ++i) if (vis[id[i]]) tmp[++j] = id[i];
for (mid = l + j - 1, i = l; i <= r; ++i) if (!vis[id[i]]) tmp[++j] = id[i];
for (i = l; i <= r; ++i) id[i] = tmp[i - l + 1];
for (i = 1; i <= n; ++i)
if (vis[i])
for (j = 1; j <= n; ++j)
if (i != j && !vis[j]) mincut[i][j] = mincut[j][i] = min(mincut[i][j], d);
Solve(l, mid), Solve(mid + 1, r);
}
int main() {
register int i, j, u, v, w, test;
scanf("%d", &test);
while (test--) {
memset(first, -1, sizeof(first)), cnt = 0;
scanf("%d%d", &n, &m);
for (i = 1; i <= m; ++i) scanf("%d%d%d", &u, &v, &w), Add(u, v, w);
for (i = 1; i <= n; ++i) id[i] = i;
memset(mincut, 63, sizeof(mincut)), Solve(1, n), scanf("%d", &w);
while (w--) {
scanf("%d", &u), v = 0;
for (i = 1; i <= n; ++i)
for (j = i + 1; j <= n; ++j) v += mincut[i][j] <= u;
printf("%d\n", v);
}
putchar('\n');
}
return 0;
}
参考文献
- 某鸽姓选手的网络流课件
2. 垃圾博主自己yy
BZOJ2229: [Zjoi2011]最小割(最小割树)的更多相关文章
- [bzoj2229][Zjoi2011]最小割_网络流_最小割树
最小割 bzoj-2229 Zjoi-2011 题目大意:题目链接. 注释:略. 想法: 在这里给出最小割树的定义. 最小割树啊,就是这样一棵树.一个图的最小割树满足这棵树上任意两点之间的最小值就是原 ...
- bzoj2229: [Zjoi2011]最小割(分治最小割+最小割树思想)
2229: [Zjoi2011]最小割 题目:传送门 题解: 一道非常好的题目啊!!! 蒟蒻的想法:暴力枚举点对跑最小割记录...绝对爆炸啊.... 开始怀疑是不是题目骗人...难道根本不用网络流?? ...
- scu - 3254 - Rain and Fgj(最小点权割)
题意:N个点.M条边(2 <= N <= 1000 , 0 <= M <= 10^5),每一个点有个权值W(0 <= W <= 10^5),现要去除一些点(不能去掉 ...
- 算法笔记--最大流和最小割 && 最小费用最大流 && 上下界网络流
最大流: 给定指定的一个有向图,其中有两个特殊的点源S(Sources)和汇T(Sinks),每条边有指定的容量(Capacity),求满足条件的从S到T的最大流(MaxFlow). 最小割: 割是网 ...
- 3532: [Sdoi2014]Lis 最小字典序最小割
3532: [Sdoi2014]Lis Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 865 Solved: 311[Submit][Status] ...
- HDU 1394 Minimum Inversion Number(最小逆序数 线段树)
Minimum Inversion Number [题目链接]Minimum Inversion Number [题目类型]最小逆序数 线段树 &题意: 求一个数列经过n次变换得到的数列其中的 ...
- POJ 3659 Cell Phone Network / HUST 1036 Cell Phone Network(最小支配集,树型动态规划,贪心)-动态规划做法
POJ 3659 Cell Phone Network / HUST 1036 Cell Phone Network(最小支配集,树型动态规划,贪心) Description Farmer John ...
- 【BZOJ2229】[Zjoi2011]最小割 最小割树
[BZOJ2229][Zjoi2011]最小割 Description 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有 ...
- 紫书 例题 11-2 UVa 1395(最大边减最小边最小的生成树)
思路:枚举所有可能的情况. 枚举最小边, 然后不断加边, 直到联通后, 这个时候有一个生成树.这个时候,在目前这个最小边的情况可以不往后枚举了, 可以直接更新答案后break. 因为题目求最大边减最小 ...
- BZOJ2229[Zjoi2011]最小割——最小割树
题目描述 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分 ...
随机推荐
- 我编写 33 个 VSCode 扩展的原因以及管理扩展的经验
简评:使用工具的同时自己创造一些工具或扩展,是一件很棒的事情. 以下"我"指原作者 Fabio 大家好,我叫 Fabio,是一位自学成才的开发人员,热衷于开源和授权.我也喜欢自己制 ...
- 耗时 2 年,用 8.5 万块乐高积木最牛复刻 Apple Park
简评:国外大佬复刻 Apple Park,看了一下细节,确实厉害!只有你想不到,没有乐高拼不起来的,有没有乐高大神挑战一下? 苹果公园以各种各样的方式鼓舞人心,让人感兴趣.从建筑.可持续性和成本,到像 ...
- Error:(1, 1) 错误: 需要class, interface或enum
这个东西在Ideal里面报的错误,在控制台提示: Error:(1, 1) 错误: 需要class, interface或enum 网上搜到说是编码问题,我的解决方式: 把出错的文件选中复制一份,再随 ...
- Java_锁Synchronized
锁(synchronized):既然线程之间是并发执行,就必然会有资源冲突的时候,如果不加以限制,很可能会出现死锁现象,这时就需要锁来对线程获取资源的限制程序中,可以给类,方法,代码块加锁.1.方法锁 ...
- 1059 C语言竞赛 (20 分)
#include <iostream> #include <iomanip> #include <cmath> using namespace std; <& ...
- html5: 复制到剪贴板 clipboard.js
1.使用clipboard.min.js工具,引用此js 注意事项: IOS微信网页开发中,若使用此工具来开发复制功能,则需要在超链接/按钮上新增 onclick=" " 2.cl ...
- 1.TypeError: must be str, not bytes
1.TypeError: must be str, not bytes错误: 解答: 写文件处 open(filename, 'w').write 应该写为 open(filename, 'wb'). ...
- centos7.x设置nginx开机自启动
设置nginx开机自启动(centos7.x) 第一步:进入到/lib/systemd/system/目录 [root@iz2z init.d]# cd /lib/systemd/system/ 第二 ...
- python-组播
#!/usr/bin/python #coding=utf-8 #发送端 import sys,struct,socket from time import sleep message="h ...
- js读取cookie信息
1. 第一种方式读取cookie信息:用document.cookie.split(“; “)的方式把字符串分割成几个段,然后遍历整个数组 //javascript方法 function getCoo ...