POJ 3417 Network

传送门

题目大意:给定一棵树和若干条边,问删去树上的一条边和给定的边中的一条使得图不联通的方案数。

题解:

首先注意到,把树上的一条边断掉变成的两个联通块,如果这两个联通块之间恰好有1条给定的边那么方案数+1.

如果没有那么再任意删去一条即可,方案数+(给定的边数)

那么一个显然的做法就是枚举n-1条边然后再枚举m条边然后统计。复杂度O(nm*统计答案的复杂度)。

显然这个东西是可以转移的,考虑dp。

假设当前正在考虑的是x和它的父节点fa的连边,x的儿子记作y。

那么考虑,显然y中有一些边可以当作x的答案,有一些不能。

哪一些不能呢?

显然是跨越y1和y2或者从y连向x的。无论哪一种都满足LCA=x。(如果LCA是x的某个孙子节点那么在那个孙子结点就会被去掉)。

所以这个题就做完了。

代码:

//POJ 3417
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN 100010
#define MAXM 200010
#define MAXL 20
using namespace std;
struct edges{
	int to,pre;
}e[MAXM];int sz[MAXN],v[MAXN];
int etop,h[MAXN],ans,mcnt;
inline int add_edge(int u,int v)
{
	etop++;
	e[etop].to=v;
	e[etop].pre=h[u];
	h[u]=etop;
	return 0;
}
inline int init_edges()
{
	memset(h,0,sizeof(h));
	return etop=0;
}
inline int inn()
{
	char ch;int x;
	while((ch=getchar())<'0'||ch>'9');
	x=ch^'0';
	while((ch=getchar())>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');
	return x;
}
int up[MAXN][MAXL],dep[MAXN];
int dfs(int x,int f)
{
	up[x][0]=f;dep[x]=dep[f]+1;
	for(int i=1;i<MAXL;i++)
		up[x][i]=up[up[x][i-1]][i-1];
	for(int i=h[x];i;i=e[i].pre)
		if(e[i].to^f) dfs(e[i].to,x);
	return 0;
}
int getLCA(int u,int v)
{
	sz[u]++,sz[v]++;
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=MAXL-1;i>=0;i--)
		if(dep[up[u][i]]>=dep[v])
			u=up[u][i];
	if(u==v) return u;
	for(int i=MAXL-1;i>=0;i--)
		if(up[u][i]!=up[v][i])
			u=up[u][i],v=up[v][i];
	return up[u][0];
}
int getans(int x,int fa)
{
	for(int i=h[x];i;i=e[i].pre)
		if(e[i].to^fa)
		{
			getans(e[i].to,x);
			sz[x]+=sz[e[i].to];
		}
	sz[x]-=(v[x]<<1);
	if(sz[x]==1) ans++;
	else if((!sz[x])&&(x^1)) ans+=mcnt;
	return 0;
	
}
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
	{
		init_edges();mcnt=m;
		for(int i=1;i<n;i++)
		{
			int u,v;scanf("%d%d",&u,&v);
			add_edge(u,v),add_edge(v,u);
		}
		int rt=1;dfs(rt,0);
		memset(v,0,sizeof(v));
		while(m--) v[getLCA(inn(),inn())]++;
		ans=0;getans(rt,0);printf("%d\n",ans);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值