SEERC 2017 L Divide and Conquer

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/83475191

题面

在这里插入图片描述

题意

给出由A,B两棵树重合后组成的一张图,问至少删掉几条边才能将图分成两块。

做法

首先,这张图一共有 n n 个点, 2 ( n 1 ) 2*(n-1) 条边,因此度数最小的点的度数一定小于等于3,这就说明了答案一定为2或3,这也就说明了A,B两棵树中一定有一棵树种只删了一条边,因此我们可以暴力枚举在A树中删掉的是哪条边,然后统计B树中有几条边连接着A树删掉这条边后的两个联通块,而这个可以用树上差分来解决:
对于B树中的每一条边 u v u-v ,令 n u m [ u ] + + , n u m [ v ] , n u m [ l c a ( u , v ) ] = 2 ( A l c a ) num[u]++,num[v]--,num[lca(u,v)]-=2(A树上的lca) .
这样A中每个点的子树 n u m num 和即为删去这个点和它父亲的连边之后两个联通块间B的边数。
因为如果 u u , v v 在同一联通块中,则它们的 l c a lca 也一定在同一联通块,不会对答案做出贡献,反之必然会对答案做出1的贡献。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define P pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define N 100100
using namespace std;

int n,ans[5],num[N],in[N],out[N],fa[N],tt;
bool vis[N];
P ba[N],bb[N];
struct Sz
{
	int sz[N];
	inline int lb(int u){return u&(-u);}
	inline void add(int u,int v){for(;u<=n;u+=lb(u)) sz[u]+=v;}
	inline int as(int u){int res=0;for(;u;u-=lb(u)) res+=sz[u];return res;}
	inline void clear(){memset(sz,0,sizeof(sz));}
	inline int ask(int u){return as(out[u])-as(in[u]-1)+1;}
};
Sz sz;
vector<int>son[N],que[N];

int ff(int u){return u==fa[u]?u:fa[u]=ff(fa[u]);}

void dfs(int now,int last)
{
	in[now]=++tt;
	int i,t;
	for(i=0;i<son[now].size();i++)
	{
		t=son[now][i];
		if(t==last) continue;
		dfs(t,now);
	}
	for(i=0;i<que[now].size();i++) if(vis[que[now][i]]) num[ff(que[now][i])]-=2;
	if(last!=-1) fa[ff(now)]=ff(last);
	out[now]=tt;
	vis[now]=1;
}

int main()
{
	int i,j,t;
	cin>>n;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&ba[i].fi,&ba[i].se);
		son[ba[i].fi].push_back(ba[i].se);
		son[ba[i].se].push_back(ba[i].fi);
	}
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&bb[i].fi,&bb[i].se);
		que[bb[i].fi].push_back(bb[i].se);
		que[bb[i].se].push_back(bb[i].fi);
		num[bb[i].fi]++,num[bb[i].se]++;
	}
	for(i=1;i<=n;i++) fa[i]=i;
	dfs(1,-1);
	for(i=1;i<=n;i++) sz.add(in[i],num[i]);
	for(i=2;i<=n;i++)
	{
		t=sz.ask(i);
		if(t<=3) ans[t]++;
	}
	if(ans[2])
	{
		cout<<2<<" "<<ans[2];
		return 0;
	}
	memset(num,0,sizeof(num));
	memset(vis,0,sizeof(vis));
	for(i=1;i<=n;i++)
	{
		fa[i]=i;
		son[i].clear();
		que[i].clear();
		sz.clear();
	}
	tt=0;
	for(i=1;i<n;i++)
	{
		son[bb[i].fi].push_back(bb[i].se);
		son[bb[i].se].push_back(bb[i].fi);
	}
	for(i=1;i<n;i++)
	{
		que[ba[i].fi].push_back(ba[i].se);
		que[ba[i].se].push_back(ba[i].fi);
		num[ba[i].fi]++,num[ba[i].se]++;
	}
	dfs(1,-1);
	for(i=1;i<=n;i++) sz.add(in[i],num[i]);
	for(i=2;i<=n;i++)
	{
		t=sz.ask(i);
		if(t<=3) ans[t]++;
	}
	cout<<3<<' '<<ans[3];
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/83475191