【BZOJ】2561: 最小生成树【网络流】【最小割】
2561: 最小生成树
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 2685 Solved: 1253
[Submit][Status][Discuss]
Description
给定一个边带正权的连通无向图G=(V,E),其中N=|V|,M=|E|,N个点从1到N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树上?
Input
第一行包含用空格隔开的两个整数,分别为N和M;
接下来M行,每行包含三个正整数u,v和w表示图G存在一条边权为w的边(u,v)。
最后一行包含用空格隔开的三个整数,分别为u,v,和 L;
数据保证图中没有自环。
Output
输出一行一个整数表示最少需要删掉的边的数量。
Sample Input
3 2
3 2 1
1 2 3
1 2 2
Sample Output
1
HINT
对于20%的数据满足N ≤ 10,M ≤ 20,L ≤ 20;
对于50%的数据满足N ≤ 300,M ≤ 3000,L ≤ 200;
对于100%的数据满足N ≤ 20000,M ≤ 200000,L ≤ 20000。
Solution
完全看不出是网络流QAQ.....
很神奇的想法...
要使加的边在最小生成树上,就要使原图中所有能连接u,v的边并且长度小于L的通路被切断,最大生成树同理。
所以就是以u,v为源汇点跑最小割。重新建边。
为什么可以建双向边?因为这道题没确定方向,所以双向边是必要的。而在bfs中从源点出发更新其他点的dep,相当于是确定了方向,所以双向边也可以跑最小割了。
#include<bits/stdc++.h>
using namespace std;
inline void read(int &x) {
x = 0; char ch = getchar();
while(ch > '9' || ch < '0') ch = getchar();
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
}
struct Node {
int u, v, f, nex;
Node(int u = 0, int v = 0, int nex = 0, int f = 0) :
u(u), v(v), nex(nex), f(f) { }
} Edge[4000005];
struct Init {
int u, v, w;
} a[200005];
int stot = 1, h[20005];
void add(int u, int v, int f) {
Edge[++stot] = Node(u, v, h[u], f);
h[u] = stot;
Edge[++stot] = Node(v, u, h[v], 0);
h[v] = stot;
}
int n, m, s, t, w;
int vis[20005], dep[20005];
bool bfs() {
memset(vis, 0, sizeof(vis));
memset(dep, 0, sizeof(dep));
queue < int > q;
q.push(s); vis[s] = 1;
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = h[u]; i; i = Edge[i].nex) {
int v = Edge[i].v;
if(!vis[v] && Edge[i].f) {
dep[v] = dep[u] + 1;
vis[v] = 1;
q.push(v);
}
}
}
return vis[t];
}
int dfs(int u, int delta) {
if(u == t || !delta) return delta;
int res = 0;
for(int i = h[u]; i && delta; i = Edge[i].nex) {
int v = Edge[i].v;
if(dep[v] == dep[u] + 1 && Edge[i].f) {
int dd = dfs(v, min(delta, Edge[i].f));
Edge[i].f -= dd;
Edge[i ^ 1].f += dd;
res += dd; delta -= dd;
}
}
return res;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i ++)
read(a[i].u), read(a[i].v), read(a[i].w);
read(s), read(t), read(w);
for(int i = 1; i <= m; i ++)
if(a[i].w < w) add(a[i].u, a[i].v, 1), add(a[i].v, a[i].u, 1);
int t1 = 0, t2 = 0;
while(bfs()) t1 += dfs(s, 0x3f3f3f3f);
stot = 1; memset(h, 0, sizeof(h));
for(int i = 1; i <= m; i ++)
if(a[i].w > w) add(a[i].u, a[i].v, 1), add(a[i].v, a[i].u, 1);
while(bfs()) t2 += dfs(s, 0x3f3f3f3f);
printf("%d", t1 + t2);
return 0;
}