Minimum Cut
Time Limit: 3000/2000 MS (Java/Others) Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 1821 Accepted Submission(s): 863
Problem Description
Given a simple unweighted graph
G
(an undirected graph containing no loops nor multiple edges) with
n
nodes and
m
edges. Let
T
be a spanning tree of
G
.
We say that a cut in G respects T if it cuts just one edges of T .
Since love needs good faith and hypocrisy return for only grief, you should find the minimum cut of graph G respecting the given spanning tree T .
We say that a cut in G respects T if it cuts just one edges of T .
Since love needs good faith and hypocrisy return for only grief, you should find the minimum cut of graph G respecting the given spanning tree T .
Input
The input contains several test cases.
The first line of the input is a single integer t (1≤t≤5) which is the number of test cases.
Then t test cases follow.
Each test case contains several lines.
The first line contains two integers n (2≤n≤20000) and m (n−1≤m≤200000) .
The following n−1 lines describe the spanning tree T and each of them contains two integers u and v corresponding to an edge.
Next m−n+1 lines describe the undirected graph G and each of them contains two integers u and v corresponding to an edge which is not in the spanning tree T .
The first line of the input is a single integer t (1≤t≤5) which is the number of test cases.
Then t test cases follow.
Each test case contains several lines.
The first line contains two integers n (2≤n≤20000) and m (n−1≤m≤200000) .
The following n−1 lines describe the spanning tree T and each of them contains two integers u and v corresponding to an edge.
Next m−n+1 lines describe the undirected graph G and each of them contains two integers u and v corresponding to an edge which is not in the spanning tree T .
Output
For each test case, you should output the minimum cut of graph
G
respecting the given spanning tree
T
.
Sample Input
1 4 5 1 2 2 3 3 4 1 3 1 4
Sample Output
Case #1: 2
Source
Recommend
wange2014
题意:
给出一个图G,求删除最少的边使的图G变为不连通,所删除的边须有且仅有一条属于图G的生成树T!
队友思路:
我们首先要考虑删除树上哪一条边,对于树上的一个结点,我们要将他们分开,就必须删除它和他父亲之间的那条边.
然后我们在考虑非树边,假设现在有一条非树边e(u,v)。 如果我们现在要删除的树边在 u到LCA(u,v) 和LCA(u,v)到v 之间,那么这条非树边的贡献即为1.(不包括 LCA(u,v))。
那么我们就可以来考虑所有的非树边,num[i] 表示删除 结点i和父亲之间的那天树边 使图不连通所需要删除的非树边条数,那么我们知道 对于一条边u,v ,有num[u]++,num[v]++,以及 u到LCA和v到LCA的路径上的点都要加一,num[LCA] -= 2; 这个过程跑个dfs计数一下即可.
具体解释一下, 这题很巧妙,他在输入的时候就统计了每条非树边对每个树边的贡献,即:要删除这个树边,要砍多少个非树边, 具体做法就是书上差分,但是这里有个坑点,因为数据水,不加也对,具体就是,每条非树边贡献的是树边在 u到LCA(u,v) 和LCA(u,v)到v 之间的边,如果砍断这些树边,要砍断这条非树边, 但是如果不在这一条路径上, 这条边是没有贡献的, 因为砍断他们俩上面的边, 他俩就分在一个联通块了, 如果差分的时候 不 cnt[lca]-=2,在统计差分的时候 这条边也影响到了上面的树边,答案显然不对
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 3e4 + 7;
const int maxm = 21;
int n, m, dep[maxn], p[maxm][maxn], head[maxn], K, ans;
int cnt[maxn];
struct node
{
int v, next;
node(){}
}edge[maxn*10];
void init()
{
memset(head, -1, sizeof(head));
memset(dep, 0, sizeof(dep));
memset(cnt, 0, sizeof(cnt));
K = 0, ans = 1e9;
}
void addEdge(int u, int v)
{
edge[K].v = v;
edge[K].next = head[u];
head[u] = K++;
}
void dfs(int u, int f, int d)
{
dep[u] = d;
p[0][u] = f;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v;
if(to == f) continue;
dfs(to, u, d+1);
}
}
void build() //构建lca
{
dfs(1, -1, 0); //第一遍统计, 1是根节点
for(int i = 0; i+1 < maxm; i++)
{
for(int v = 1; v <= n; v++)
{
if(p[i][v] < 0) p[i+1][v] = -1;
else p[i+1][v] = p[i][p[i][v]];
}
}
}
int LCA(int u, int v)
{
if(dep[u] > dep[v]) swap(u, v);
for(int i = 0; i < maxm; i++)
{
if((dep[v]-dep[u])>>i&1)
v = p[i][v];
}
if(u == v) return u;
for(int i = maxm-1; i >= 0; i--)
{
if(p[i][u] != p[i][v])
{
u = p[i][u];
v = p[i][v];
}
}
return p[0][u];
}
void solve(int u, int f)
{
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v;
if(to == f) continue;
solve(to, u);
cnt[u] += cnt[to];
}
}
int main()
{
int _, x, y, ca = 1;
cin >> _;
while(_--)
{
init();
scanf("%d%d", &n, &m);
int x, y;
for(int i = 1; i < n; i++)
{
scanf("%d%d", &x, &y);
addEdge(x, y);
}
build();
for(int i = 1; i <= m-n+1; i++)
{
scanf("%d%d", &x, &y);
cnt[x]++;
cnt[y]++;
int lca = LCA(x, y);
cnt[lca] -= 2;
}
solve(1, -1);
for(int i = 2; i <= n; i++)
ans = min(ans, cnt[i]+1);
printf("Case #%d: %d\n",ca++, ans);
}
return 0;
}