【POJ3417】Network【LCA】【差分】

版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/84976078

题目大意:

题目链接:http://poj.org/problem?id=3417
一个 n n 个结点的树加入了 m m 条边。求删除一条原边和一条新边把这个图拆成两个部分的方案数。


思路:

对于后面的 m m 条新边,我们可以把每一条原边加入一个边权(初始为 0 0 ),对于每次加入的新边 ( x , y ) (x,y) ,我们就将树上从 x x y y 的路径的边权加 1 1 。最终求答案时枚举所有的边:

  • 如果边权为 0 0 ,那么删除这条边之后树就被分成了两个不相邻的部分。那么此时删除任意一条边都满足分成两个部分。 a n s + = m ans+=m
  • 如果边权为 1 1 ,那么删除包含这条边的新边(有且仅有一条)就可以满足分成两个部分。 a n s + + ans++
  • 如果边权大于 1 1 ,无论如何都不可以满足要求。

那么我们就得到了一个 O ( n m ) O(nm) 的算法。


关于优化

记得差分吗?
我们每次将新边 ( x , y ) (x,y) 之间的每一条边的权值加 1 1 ,其实是可以利用树上差分的方法来 O ( 1 ) O(1) 搞出来的。
对于新边 ( x , y ) (x,y) ,我们将 s [ x ] + + , s [ y ] + + s [ l c a ( x , y ) ] = 2 s[x]++,s[y]++,s[lca(x,y)]-=2 。然后跑一遍 D F S DFS ,求出每一点 s [ x ] s[x] 表示它与父节点之间的边被覆盖的次数。
时间复杂度 O ( n + m ) O(n+m)


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; 
typedef long long ll;

const int N=100010;
const int LG=20;
int n,m,x,y,tot,head[N],s[N],f[N][LG+1],dep[N];
ll ans;

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void dfs1(int x,int fa)
{
	dep[x]=dep[fa]+1;
	f[x][1]=fa;
	for (int i=2;i<=LG;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	for (int i=head[x];~i;i=e[i].next)
	{
		int y=e[i].to;
		if (fa==y) continue;
		dfs1(y,x);
	}
}

int lca(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=LG;i>=1;i--)
		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
	if (x==y) return x;
	for (int i=LG;i>=1;i--)
		if (f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][1];
}

int dfs2(int x,int fa)
{
	for (int i=head[x];~i;i=e[i].next)
	{
		int y=e[i].to;
		if (fa==y) continue;
		s[x]+=dfs2(y,x);
	}
	if (x!=1)
	{
		if (s[x]==0) ans+=(ll)m;
		if (s[x]==1) ans++;
	}
	return s[x];
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs1(1,0);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		s[x]++;
		s[y]++;
		s[lca(x,y)]-=2;
	}
	dfs2(1,0);
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/84976078