zoukankan      html  css  js  c++  java
  • [CF911F] Tree Destruction

    问题描述

    You are given an unweighted tree with n vertices. Then n - 1 following operations are applied to the tree. A single operation consists of the following steps:

    1. choose two leaves;
    2. add the length of the simple path between them to the answer;
    3. remove one of the chosen leaves from the tree.

    Initial answer (before applying operations) is 0. Obviously after n - 1 such operations the tree will consist of a single vertex.

    Calculate the maximal possible answer you can achieve, and construct a sequence of operations that allows you to achieve this answer!

    输入格式

    The first line contains one integer number n (2 ≤ n ≤ 2·105) — the number of vertices in the tree.

    Next n - 1 lines describe the edges of the tree in form ai, bi (1 ≤ ai, bi ≤ n, ai ≠ bi). It is guaranteed that given graph is a tree.

    输出格式

    In the first line print one integer number — maximal possible answer.

    In the next n - 1 lines print the operations in order of their applying in format ai, bi, ci, where ai, bi — pair of the leaves that are chosen in the current operation (1 ≤ ai, bi ≤ n), ci (1 ≤ ci ≤ n, ci = ai or ci = bi) — choosen leaf that is removed from the tree in the current operation.

    See the examples for better understanding.

    样例输入

    3
    1 2
    1 3

    样例输出

    3
    2 3 3
    2 1 1

    解析

    首先我们要知道一个性质:无权树上任意一个节点,与它距离最远的节点一定是直径的某个端点。所以,利用贪心的思想,每次对一个叶节点都是用直径的两个端点离它最远的点计算答案,并且删掉当前叶节点。当只剩下直径的时候,就一个一个删点知道剩下最后一个点。

    代码

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #define int long long
    #define N 200002
    using namespace std;
    queue<int> q;
    int head[N],ver[N*2],nxt[N*2],l;
    int n,i,dis1[N],dis2[N],a,b,maxx,son[N],fa[N],ans[N][3],cnt;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y)
    {
    	l++;
    	ver[l]=y;
    	nxt[l]=head[x];
    	head[x]=l;
    	son[x]++;
    }
    void dfs(int x,int pre,int d)
    {
    	if(d>maxx) maxx=d,a=x;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre) dfs(y,x,d+1);
    	}
    	if(son[x]==1) q.push(x);
    }
    void dfs1(int x,int pre)
    {
    	if(dis1[x]>maxx) maxx=dis1[x],b=x;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre){
    			fa[y]=x;
    			dis1[y]=dis1[x]+1;
    			dfs1(y,x);
    		}
    	}
    }
    void dfs2(int x,int pre)
    {
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre){
    			dis2[y]=dis2[x]+1;
    			dfs2(y,x);
    		}
    	}
    }
    signed main()
    {
    	n=read();
    	for(i=1;i<n;i++){
    		int u=read(),v=read();
    		insert(u,v);
    		insert(v,u);
    	}
    	dfs(1,0,0);
    	maxx=0;
    	dfs1(a,0);
    	dfs2(b,0);
    	int sum=0,d=dis1[b];
    	while(!q.empty()){
    		while(q.front()==a||q.front()==b) q.pop();
    		if(q.empty()) break;
    		int x=q.front();
    		q.pop();
    		sum+=max(dis1[x],dis2[x]);
    		son[x]--;
    		for(i=head[x];i;i=nxt[i]){
    			son[ver[i]]--;
    			if(son[ver[i]]==1) q.push(ver[i]);
    		}
    		cnt++;
    		ans[cnt][0]=ans[cnt][2]=x;
    		if(dis1[x]>dis2[x]) ans[cnt][1]=a;
    		else ans[cnt][1]=b;
    	}
    	while(b!=a){
    		cnt++;
    		ans[cnt][0]=a;ans[cnt][1]=b;ans[cnt][2]=b;
    		b=fa[b];
    		sum+=d;
    		d--;
    	}
    	printf("%lld
    ",sum);
    	for(i=1;i<=cnt;i++) printf("%lld %lld %lld
    ",ans[i][0],ans[i][1],ans[i][2]);
    	return 0;
    }
    

    总结

    这里有一个很好的性质值得记忆。

  • 相关阅读:
    面向对象程序设计寒假作业2
    终于开通了园子里的贴号
    java中与类继承相关的知识点
    动态代理机制之查看一个类或接口中有哪些方法
    动态代理
    xml与html的区别
    深入理解linux i节点(inode)
    netstat命令
    关于C++ const 的全面总结
    linux 下 进程和线程的区别
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11684891.html
Copyright © 2011-2022 走看看