3230. 【佛山市选2013】树环转换

Description

给定一棵N个节点的树,去掉这棵树的一条边需要消耗值1,为这个图的两个点加上一条边也需要消耗值1。树的节点编号从1开始。在这个问题中,你需要使用最小的消耗值(加边和删边操作)将这棵树转化为环,不允许有重边。

环的定义如下:

(1)该图有N个点,N条边。

(2)每个顶点的度数为2。

(3)任意两点是可达的。

树的定义如下:

(1)该图有N个点,N-1条边。

(2)任意两点是可达的。

Input

第一行是一个整数N代表节点的个数。

接下来N-1行每行有两个整数U, V(1 ≤ U, V ≤ N),表示双向边(U, V)

Output

输出把树转化为环的最小消耗值。

Sample Input

4

1 2

2 3

2 4

Sample Output

3

Data Constraint

对于20%的数据,有1≤N≤10。

对于100%的数据,有1≤N≤1000000。

Solution

树形dp。

考虑将问题转化,我们可以先将树转化成一条链,然后再加上一条边构成一个环。

设f[ i ][0/1]表示以i为根的是否是链上的一个端点的最小答案。

 

Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define N 1000005
using namespace std;
I n,x,y,tot,rt,now,val,f[N][2],g[N][2],s[N],sz[N],st[N],tp,son,t[N<<1],nx[N<<1],ls[N];
void add(I x,I y){t[++tot]=y,nx[tot]=ls[x],ls[x]=tot;}
I main(){
	freopen("transformation.in","r",stdin);
	freopen("transformation.out","w",stdout);
	scanf("%d",&n);
	F(i,1,n-1){
		g[i][0]=g[i][1]=-N*5;
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	g[n][0]=g[n][1]=-N*5;
	st[tp=1]=1;
	while(tp){
		x=st[tp];
		if(!ls[x]){
			if(sz[x]) f[x][1]=f[x][0]=min(s[x]+2*sz[x],s[x]-g[x][0]+2*(sz[x]-1));
			if(sz[x]>1) f[x][1]=min(f[x][1],s[x]-g[x][0]-g[x][1]+2*(sz[x]-2));
			son=x;x=st[--tp];
			if((val=f[son][1]-f[son][0])>g[x][0]){g[x][1]=g[x][0],g[x][0]=val;}
			else g[x][1]=max(g[x][1],val);
			s[x]+=f[son][1],sz[x]++;
		}
		else{
			for(I k=ls[x];k;k=nx[k]){
				ls[x]=nx[k];
				if(t[k]!=st[tp-1]){st[++tp]=t[k];break;}
			}
		}
	}
	printf("%d\n",min(f[1][0],f[1][1])+1);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/107870617