HDU - 5452 Minimum Cut LCA + imos和

54 篇文章 0 订阅
4 篇文章 0 订阅

传送门:HDU5452

题意:给出一个无向图G,求图G的最小边割集,并且使得割边中有且仅有一条属于图G的生成树T。

思路:考虑生成树中的每一条边i,将其去掉后生成树就被划分成两部分,则全图的割边就是那些跨越这两部分的边,将这些割边的数量记录为ans[i],但是求ans[]的过程显然不能暴力,考虑每条不在生成树中的边对答案的贡献,设其端点为u,v,则u -> v路径上所有边对应的ans[i]都应该++,但是这个过程也不能暴力,这时候就要用到imos和的思想了,详见:点击打开链接

也就是说我们只需要在ans[u]++, ans[v]++, ans[lca(u, v)] -= 2,然后最后再求一遍和就行了。

PS:我感觉这种做法是这个题的正解,看网上好多做法都是水过去的。

代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 20050
#define MAXM 200050
#define inf 0x3f3f3f3f
typedef pair<int,int>P;
int dep[MAXN], ans[MAXN];
int f[20][MAXN], pre[MAXN];
int cnt;
struct node
{
    int v, next;
	node(int _v = 0, int _next = 0) : v(_v), next(_next) {}
}mp[MAXM * 2];

void init()
{
    cnt = 0;
    memset(pre, -1, sizeof(pre));
    memset(ans, 0, sizeof(ans));
}

void add(int u, int v)
{
	mp[cnt] = node(v, pre[u]), pre[u] = cnt++;
	mp[cnt] = node(u, pre[v]), pre[v] = cnt++;
}

void dfs(int u, int fa)
{
    dep[u] = dep[fa] + 1;
    for(int i = pre[u]; ~i; i = mp[i].next)
    {
        int v = mp[i].v;
        if(v == fa) continue;
        dfs(v, u);
        f[0][v] = u;
    }
}

int lca_init(int n)
{
	dep[1] = 1;
    dfs(1, 0);
    int k = 0,t = 1;
    while(t <= n)t <<= 1,k++;
    for(int i=0;i+1<k;i++)
    {
        for(int j=1;j<=n;j++)
        {
           f[i+1][j] = f[i][f[i][j]];
        }
    }
    return k;
}

int lca(int u, int v, int MAX)
{
    if(dep[u] < dep[v]) swap(u, v);
    int k = dep[u] - dep[v];
    for(int i=0;i<MAX;i++)
    {
        if((k >> i) & 1)
        u = f[i][u];
    }
    if(u == v)return u;
    for(int i=MAX-1;i>=0;i--)
    while(f[i][u] != f[i][v])
    {
        u = f[i][u];
        v = f[i][v];
    }
    return f[0][u];
}

void get_ans(int u, int fa)
{
    for(int i = pre[u]; ~i; i = mp[i].next)
    {
        int v = mp[i].v;
        if(v == fa) continue;
        get_ans(v, u);
        ans[u] += ans[v];
    }
}

int main()
{
    int T, u, v, n, m, kase = 1;
    cin >> T;
    while(T--)
    {
    	scanf("%d %d", &n, &m);
        init();
        for(int i = 0; i < n - 1; i++)
        {
        	scanf("%d %d", &u, &v);
        	add(u, v);
		}
        int up = lca_init(n), fa;
        for(int i = n - 1; i < m; i++)
        {
            scanf("%d %d",&u, &v);
            fa = lca(u, v, up);
            ans[u]++, ans[v]++;
            ans[fa] -= 2;
        }
        get_ans(1, -1);
        printf("Case #%d: %d\n", kase++, *min_element(ans + 2, ans + n + 1) + 1);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值