• CF911F Tree Destruction


    题意翻译

    给你一棵树,每次挑选这棵树的两个叶子,加上他们之间的边数(距离),然后将其中一个点去掉,问你边数(距离)之和最大可以是多少.

    题目描述

    You are given an unweighted tree with n n n vertices. Then n−1 n-1 n1 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 0 0 . Obviously after n−1 n-1 n1 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 n n ( 2<=n<=2⋅105 2<=n<=2·10^{5} 2<=n<=2105 ) — the number of vertices in the tree.

    Next n−1 n-1 n1 lines describe the edges of the tree in form ai,bi a_{i},b_{i} ai,bi ( 1<=ai 1<=a_{i} 1<=ai , bi<=n b_{i}<=n bi<=n , ai≠bi a_{i}≠b_{i} aibi ). 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 n-1 n1 lines print the operations in order of their applying in format ai,bi,ci a_{i},b_{i},c_{i} ai,bi,ci , where ai,bi a_{i},b_{i} ai,bi — pair of the leaves that are chosen in the current operation ( 1<=ai 1<=a_{i} 1<=ai , bi<=n b_{i}<=n bi<=n ), ci c_{i} ci ( 1<=ci<=n 1<=c_{i}<=n 1<=ci<=n , ci=ai c_{i}=a_{i} ci=ai or ci=bi c_{i}=b_{i} ci=bi ) — choosen leaf that is removed from the tree in the current operation.

    See the examples for better understanding.

    输入输出样例

    输入样例#1: 
    3
    1 2
    1 3
    
    输出样例#1: 
    3
    2 3 3
    2 1 1
    
    输入样例#2: 
    5
    1 2
    1 3
    2 4
    2 5
    
    输出样例#2: 
    9
    3 5 5
    4 3 3
    4 1 1
    4 2 2
    

    Solution:

       贪心+树的直径。

      dfs处理出树的直径,然后先删去非直径上的分枝的叶子节点,再删直径。

      树的直径一定是树上最长的一条简单路径,然后每次删掉非直径上的叶子节点的最远距离,一定是该节点与直径两端点中的某一个搭配出的最长距离,证明就一句话,不可能存在两个都是非直径上的叶子节点的路径超过直径的长度,画图或者脑补,其实蛮简单的贪心,就是难想到。

      具体实现时,两遍dfs处理出直径,再dfs一遍处理出各节点到直径两端的距离,直径上的节点对答案的总贡献直接等差数列求和,非直径上的点直接累加到直径两段距离的最大值,然后输出方案,我的做法是将非直径上的节点和直径两端点的搭配都压入堆中,以距离为关键字,每次弹出就输出方案,最后只剩直径时就是固定一端依次删另一端就好了。

    代码:

    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
    using namespace std;
    const int N=4e5+7;
    int n,to[N],net[N],h[N],cnt;
    int tp[N],q[N],tot,maxn;
    ll dis1[N],dis2[N],ans;
    bool vis[N];
    struct node{
        int u,v;
        ll d;
        node(int a=0,int b=0,ll c=0){u=a;v=b;d=c;}
        bool operator<(const node &a)const {return d<a.d;}
    };
    priority_queue<node>Q;
    
    il int gi(){
        int a=0;char x=getchar();
        while(x<'0'||x>'9')x=getchar();
        while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
        return a;
    }
    
    il void add(int u,int v){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt;}
    
    il void dfs1(int u,int lst){
        for(int i=h[u];i;i=net[i])
            if(!vis[to[i]]) vis[to[i]]=1,dfs1(to[i],lst+1),vis[to[i]]=0;
        if(lst>maxn) q[tot=1]=u,maxn=lst;
    }
    
    il void dfs2(int u,int lst){
        for(int i=h[u];i;i=net[i])
            if(!vis[to[i]]) tp[lst]=to[i],vis[to[i]]=1,dfs2(to[i],lst+1),vis[to[i]]=0;
        if(lst>tot) {
            tot=lst;
            For(i,2,lst) q[i]=tp[i];
        }
    }
    
    il void dfs3(int u,ll *a){
        for(int i=h[u];i;i=net[i])
            if(!vis[to[i]]) a[to[i]]=a[u]+1,vis[to[i]]=1,dfs3(to[i],a),vis[to[i]]=0;
    }
    
    int main(){
        n=gi();
        int u,v;
        For(i,1,n-1) u=gi(),v=gi(),add(u,v),add(v,u);
        vis[1]=1,dfs1(1,0),tot=0,vis[1]=0,vis[q[1]]=1,dfs2(q[1],2),tot--;
        dfs3(q[1],dis1),vis[q[1]]=0,vis[q[tot]]=1,dfs3(q[tot],dis2);
        For(i,1,tot) vis[q[i]]=1;
        ans=1ll*tot*(tot-1)/2;
        For(i,1,n)
            if(!vis[i])ans+=max(dis1[i],dis2[i]),Q.push(node(q[1],i,dis1[i])),Q.push(node(q[tot],i,dis2[i]));
        printf("%lld
    ",ans);
        while(!Q.empty()){
            node a=Q.top();Q.pop();
            if(!vis[a.v]) printf("%d %d %d
    ",a.u,a.v,a.v),vis[a.v]=1;
        }
        Bor(i,2,tot) printf("%d %d %d
    ",q[1],q[i],q[i]);
        return 0;
    }
     
  • 相关阅读:
    学习:大文件统计与排序
    共享库SidebySide之Windows Shared Assembly
    Bundle是个好东西
    所谓的代码段、数据段
    [design decision] common case vs. rare case
    如何给C++设计一个GC
    玩一把tesseract
    [design decision]让工具依赖于naming convention是个拙劣的做法
    监控域名可用性并自动发信
    调试lua代码
  • 原文地址:https://www.cnblogs.com/five20/p/9369698.html
Copyright © 2020-2023  润新知