HDU - 5452 Minimum Cut (lca+差分+思维)

42 篇文章 0 订阅
8 篇文章 0 订阅


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 .
 

Input
The input contains several test cases.
The first line of the input is a single integer  t (1t5)  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 (2n20000)  and  m (n1m200000) .
The following  n1  lines describe the spanning tree  T  and each of them contains two integers  u  and  v  corresponding to an edge.
Next  mn+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
 

Statistic |  Submit |  Discuss |  Note


题意:
给出一个图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;
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值