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;
}