Description
给定一棵N个节点的树,去掉这棵树的一条边需要消耗值1,为这个图的两个点加上一条边也需要消耗值1。树的节点编号从1开始。在这个问题中,你需要使用最小的消耗值(加边和删边操作)将这棵树转化为环,不允许有重边。
环的定义如下:
(1)该图有N个点,N条边。
(2)每个顶点的度数为2。
(3)任意两点是可达的。
树的定义如下:
(1)该图有N个点,N-1条边。
(2)任意两点是可达的。
环的定义如下:
(1)该图有N个点,N条边。
(2)每个顶点的度数为2。
(3)任意两点是可达的。
树的定义如下:
(1)该图有N个点,N-1条边。
(2)任意两点是可达的。
Input
第一行是一个整数N代表节点的个数。
接下来N-1行每行有两个整数U, V(1 ≤ U, V ≤ N),表示双向边(U, V)
接下来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。
对于100%的数据,有1≤N≤1000000。
分析
早上写的时候想的是奇怪的DP
然后搞了一阵子发现可以贪心?
只有一个儿子的节点则保留不变,值上传
两个儿子的节点可以断开与父亲的联系,将子树中所有点连成一条链的代价和断开再连接的代价加入答案
三及以上个儿子的节点可以通过断开若干个儿子的关系转变成两个儿子的状态
这里默认树根是某条链的一端,所以如果树根属于后两种状态时,无需加上断开和连接的代价
最后不要忘了加连接链两端的代价1
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <cstdio> using namespace std; const int N=1e6+10; struct Graph { int v,nx; }g[2*N]; int cnt,list[N]; int n,ans; int f[N]; int stk[N][3],top; bool b[N]; void Add(int u,int v) { g[++cnt]=(Graph){v,list[u]};list[u]=cnt; g[++cnt]=(Graph){u,list[v]};list[v]=cnt; } void DFS() { stk[++top][0]=1;stk[top][1]=0;stk[top][2]=0; while (top) { int u=stk[top][0]; if (!list[u]) { if (stk[top][2]>=2) ans+=f[u]+(stk[top][1]!=0?2:0); b[u]=stk[top][2]<2; top--; u=stk[top][0]; if (b[g[list[u]].v]) { stk[top][2]++; f[u]+=f[g[list[u]].v]; if (stk[top][2]>2) f[u]+=2; } list[u]=g[list[u]].nx; } for (int i=list[u];i;i=list[u]=g[i].nx) if (g[i].v!=stk[top][1]) { stk[++top][0]=g[i].v,stk[top][1]=u,stk[top][2]=0; break; } } } int main() { scanf("%d",&n); for (int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Add(u,v); DFS(); printf("%d",ans+1); }