2561: 最小生成树
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 957 Solved: 483
[ 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;
数据保证图中没有自环。
接下来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
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。
考虑Kruskal算法de过程,若想让u,v出现在最小生成树中,则将边按权值排序后,比u-v的边权小的那些边不能使u和v联通,因此考虑最小割,求两遍最小割即可。
/**************************************************************
Problem: 2561
User: mogu
Language: C++
Result: Accepted
Time:616 ms
Memory:12992 kb
****************************************************************/
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
const int MAXN = 40000 + 10;
const int MAXM = 400000 + 10;
const int INF = 0x3f3f3f3f;
int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f *= -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
struct Edge
{int to, next, cap, flow;}edge[MAXM];
int tot, head[MAXN];
int gap[MAXN], dep[MAXN], pre[MAXN], cur[MAXN];
void init(){tot = 0; memset(head, -1, sizeof(head));}
void addedge(int u, int v, int w, int rw = 0)
{
edge[tot].to = v; edge[tot].cap = w; edge[tot].next = head[u];
edge[tot].flow = 0; head[u] = tot++;
edge[tot].to = u; edge[tot].cap = rw; edge[tot].next = head[v];
edge[tot].flow = 0; head[v] = tot++;
}
long long sap(int start, int end, int N)
{
memset(gap, 0, sizeof(gap));
memset(dep, 0, sizeof(dep));
memcpy(cur, head, sizeof(head));
int u = start; pre[u] = -1; gap[0] = N;
long long ans = 0;
while(dep[start] < N)
{
if(u == end)
{
long long Min = INF;
for(int i=pre[u];i!=-1;i=pre[edge[i^1].to])
if(Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
for(int i=pre[u];i!=-1;i=pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start; ans += Min; continue;
}
bool flag = false;
int v;
for(int i=cur[u];i!=-1;i=edge[i].next)
{
v = edge[i].to;
if(edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
{
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if(flag)
{u = v; continue;}
int Min = N;
for(int i=head[u];i!=-1;i=edge[i].next)
{
if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min)
{
Min = dep[edge[i].to];
cur[u] = i;
}
}
gap[dep[u]]--;
if(!gap[dep[u]]) return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if(u != start) u = edge[pre[u]^1].to;
}
return ans;
}
int n, m, U, V, L;
int u[MAXM], v[MAXM], w[MAXM];
int main()
{
n = read(); m = read();
for(int i=1;i<=m;i++)
{
u[i] = read(), v[i] = read(), w[i] = read();
}
long long ans = 0;
U = read(), V = read(), L = read();
init();
for(int i=1;i<=m;i++)
{
if(w[i] < L) addedge(u[i], v[i], 1, 1);
}
ans += sap(U, V, n);
init();
for(int i=1;i<=m;i++)
{
if(w[i] > L) addedge(u[i], v[i], 1, 1);
}
ans += sap(U, V, n);
printf("%lld\n", ans);
return 0;
}