• 洛谷 P3629 [APIO2010]巡逻(树的直径,贪心)


    传送门


    解题思路

    我们先考虑k==1的情况,这时候一定选取的是树的直径的两个端点。

    因为不加这条新路每个边一定走两次,加上了之后道路两个端点之间的路径只需走一遍,所以贪心思想,一定是选取最长的路径的两个端点,即树的直径。

    再考虑k==2的情况,一种情况是新加的道路的两个端点之间的路径与加的第一条边两个端点之间的路径没有重边,这样和第一种情况一样,去直径;

    另一种情况是新加的道路的两个端点之间的路径与上一次的路径有重复的边,我们发现,这一次不但没有少走一次,反而还要多走一次,所以我们可以把边权改成-1,再做一次树的直径。

    因为第一次加道路求树的直径需要记录直径上的点,所以要用两次dfs求;

    而第二次加道路因为树上有负边权-1,所以dfs无法求,只能用树形dp求解。

    最后注意答案的统计。

    AC代码

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cmath>
     4 #include<cstdio>
     5 #include<cstring>
     6 using namespace std;
     7 const int maxn=100005;
     8 struct node {
     9     int v,next,value;
    10 }e[maxn*2];
    11 int n,cnt,p[maxn],pre[maxn],maxd,k,ans,dis[maxn],dp[maxn],maxqaq=-2;
    12 void insert(int u,int v,int value){
    13     cnt++;
    14     e[cnt].v=v;
    15     e[cnt].next=p[u];
    16     e[cnt].value=value;
    17     p[u]=cnt;
    18 } 
    19 void dfs(int u,int fa){
    20     for(int i=p[u];i!=-1;i=e[i].next){
    21         int v=e[i].v;
    22         if(v==fa){
    23             pre[u]=fa;
    24             continue;
    25         }
    26         dis[v]=dis[u]+e[i].value;
    27         dfs(v,u);
    28         if(dis[v]>dis[maxd]) maxd=v;
    29     }
    30 }
    31 void dfs2(int u,int fa){
    32     for(int i=p[u];i!=-1;i=e[i].next){
    33         int v=e[i].v;
    34         if(v==fa){
    35             continue;
    36         }
    37         dfs2(v,u);
    38         maxqaq=max(maxqaq,dp[u]+dp[v]+e[i].value);
    39         dp[u]=max(dp[u],dp[v]+e[i].value);
    40     }
    41 }
    42 int main()
    43 {
    44     memset(p,-1,sizeof(p));
    45     cin>>n>>k;
    46     for(int i=1;i<n;i++){
    47         int u,v;
    48         scanf("%d%d",&u,&v);
    49         insert(u,v,1);
    50         insert(v,u,1);
    51     }
    52     memset(dis,0,sizeof(dis));
    53     maxd=1;
    54     dfs(1,-1);
    55     int d1=maxd;
    56     memset(dis,0,sizeof(dis));
    57     dfs(d1,-1);
    58     int d2=maxd;
    59     ans+=dis[d2];
    60     ans=2*n-ans-1;
    61     if(k==1) cout<<ans;
    62     else{
    63         for(int u=d2;u!=d1;u=pre[u]){
    64             for(int i=p[u];i!=-1;i=e[i].next){
    65                 if(e[i].v==pre[u]) e[i].value=-1;
    66             }
    67             for(int i=p[pre[u]];i!=-1;i=e[i].next){
    68                 if(e[i].v==u) e[i].value=-1;
    69             }
    70         }
    71         dfs2(1,-1);
    72         ans=ans-maxqaq+1;
    73         cout<<ans;
    74     }
    75     
    76     return 0;
    77 }

    //APIO2010 t2

  • 相关阅读:
    c++中单引号和双引号的区别
    C++ 数组
    C++ 输出到文本文件
    C++中文本的读入
    C++ 输入和输出
    C++构造函数和文件组织
    Linux中使用gcc编译文件
    linux下修改gcc编译器版本
    Git--创建与合并分支
    'webpack' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/13818080.html
Copyright © 2020-2023  润新知