HDU 5452 Minimum Cut(LCA+DFS)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5452


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
2015 ACM/ICPC Asia Regional Shenyang Online

题意:

给出一个图G,求删除最少的边使的图G变为不连通,所删除的边须有且仅有一条属于图G的生成树T!


PS:

如果一条边 e(u,v) 不属于生成树T,那么对于 u 和  v 来说它连接到其他子树的贡献是1,

所以要num[u]++, num[v]++; (num存的是删除某个点需要删除的边的数量)! 

如果e(u,v)这条边不是LCA(u,v)的这个点所要删除的边。那么两个端点的贡献就是2!所以要num[tt]-=2;


代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 80010;
const int maxm = 26;
int dp[2*maxn][maxm];  //数组开到2*N,因为遍历后序列长度为2*n-1
bool vis[maxn];
struct edge
{
    int from, to;
    int next;
} e[2*maxn];
int tot,head[maxn];
int cnt;
int num[maxn];
void init()
{
    memset(head,-1,sizeof(head));
    memset(vis,false,sizeof(vis));
    memset(num,0,sizeof(num));
    cnt = 0;
}
void addedge(int u, int v)
{
    e[cnt].from = u;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
int ver[2*maxn], R[2*maxn], first[maxn];
//ver:节点编号 R:深度 first:点编号位置
void dfs(int u ,int dep)
{
    vis[u] = true;
    ver[++tot] = u;
    first[u] = tot;
    R[tot] = dep;
    for(int k=head[u]; k!=-1; k=e[k].next)
        if( !vis[e[k].to] )
        {
            int v = e[k].to;
            dfs(v, dep+1);
            ver[++tot] = u;
            R[tot] = dep;
        }
}
void ST(int n)
{
    for(int i=1; i<=n; i++)
        dp[i][0] = i;
    for(int j=1; (1<<j)<=n; j++)
    {
        for(int i=1; i+(1<<j)-1<=n; i++)
        {
            int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
            dp[i][j] = R[a]<R[b]?a:b;
        }
    }
}
//中间部分是交叉的。
int RMQ(int l,int r)
{
    int k=0;
    while((1<<(k+1))<=r-l+1)
        k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k]; //保存的是编号
    return R[a]<R[b]?a:b;
}

int LCA(int u ,int v)
{
    int x = first[u] , y = first[v];
    if(x > y) swap(x,y);
    int res = RMQ(x,y);
    return ver[res];
}

int DFS(int u,int fa)
{
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == fa)
            continue;
        DFS(v, u);
        num[u]+=num[v];
    }
    return 0;
}

int main()
{
    int t;
    int cas = 0;
    int n, m;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        int u, v;
        for(int i = 0; i < n-1; i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u, v);
            addedge(v, u);
        }
        for(int i = n; i <= m; i++)
        {
            scanf("%d%d",&u,&v);
            int tt = LCA(u, v);
            num[u]++;
            num[v]++;
            num[tt]-=2;//一条边两个端点 贡献为2
        }
        DFS(1, 1);
        int ans = INF;
        for(int i = 2; i <= n; i++)
        {
            ans = min(ans, num[i]+1);
        }
        printf("Case #%d: %d\n",++cas,ans);
    }
    return 0;
}


你可能感兴趣的:(HDU,网络赛,2015网络赛)