POJ 3417 Network LCA

22 篇文章 0 订阅
  这题的意思是给N个点,新建M条边。先给一幅图,有N-1条边,然后给新建边M条,问删去一条原有边,一条新建边,有几种方法把图分成两块。这题思路是这样的,新建边与新建边两点的先祖节点形成的环上的边加1,因为新建边保护了环上的边,使得断了后图不会分成两块,所以当原有边次数为1时,只有断保护它的新建边才会使图分成两部分,如果原有图的边为零,因为无新建边保护,所以断开任意新建边都可以使图分成两部分,若次数大于1,无论怎样都不会使图分成两部分。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define maxn 100005
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;

int head[maxn], qhead[maxn], lca[maxn], dp[maxn], qeagenum, eagenum;
bool vis[maxn], vis2[maxn];

struct eage
{
	int to;
	int next;
}eg[maxn * 2];

void add(int u, int v)
{
	eg[eagenum].to = v;
	eg[eagenum].next = head[u];
	head[u] = eagenum++;
	return;
}

struct qeage
{
	int to;
	int num;
	int next;
}qeg[maxn * 2];

void qadd(int u, int v, int num)
{
	qeg[qeagenum].to = v;
	qeg[qeagenum].num = num;
	qeg[qeagenum].next = qhead[u];
	qhead[u] = qeagenum++;
	return;
}

void inits()
{
	mem(vis, 0);
	mem(vis2, 0);
	mem(dp, 0);
	mem(lca, 0);
	mem(head, -1);
	mem(qhead, -1);
	eagenum = 0;
	qeagenum = 0;
	return;
}

int finds(int a)
{
	return a == lca[a] ? a : finds(lca[a]);
}

void tarjan(int a)
{
	lca[a] = a;
	vis[a] = 1;
	int i, v;
	for(i = head[a];i != -1;i = eg[i].next)
	{
		v = eg[i].to;
		if(vis[v])
		continue;
		tarjan(v);
		lca[v] = a;
	}
	for(i = qhead[a];i != -1;i = qeg[i].next)
	{
		v = qeg[i].to;
		if(vis[v]&&!vis2[qeg[i].num])
		{
			vis2[qeg[i].num] = 1;
			dp[finds(v)] -= 2;
		}
	}
	return;
}

void DP(int u)
{
	vis[u] = 1;
	for(int i = head[u];i != -1;i = eg[i].next)
	{
		int v = eg[i].to;
		if(!vis[v])
		{
			DP(v);
			dp[u] += dp[v];
		}
	}
	return;
}

int main(int argc, char *argv[])
{
    int n, m, qn, u, v, ans;
    while(~scanf("%d%d", &n, &m))
    {
    	inits();
    	ans = 0;
    	for(int i = 0;i < (n - 1);i++)
    	{
	    	scanf("%d%d", &u, &v);
	    	add(u, v);
	    	add(v, u);
	    }
	    for(int i = 0;i < m;i++)
	    {
    		scanf("%d%d", &u, &v);
    		qadd(u, v, i);
    		qadd(v, u, i);
    		dp[u]++;
    		dp[v]++;
    	}
    	tarjan(1);
    	mem(vis, 0);
    	DP(1);
    	for(int i = 2;i <= n;i++)
    	{
	    	if(dp[i] == 1)
	    	ans += 1;
	    	else if(!dp[i])
	    	ans += m;
	    }
	    printf("%d\n", ans);
    }
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值