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


    题目链接:https://www.luogu.com.cn/problem/P3629

    首先如果不添加任何道路,每条边会经过两次,那么所经过的路径长度应该是2*(n-1)。

    分析可得,当添加一条道路时,会形成一个环,这个环上且属于原来树上的点只会经过一次。所以可以将直径的两个端点连起来,会使减小的路径最大。

    因此可以得到k=1的思路:两次DFS求出树的直径d,输出2*(n-1)-(d-1)。

    当添加两条道路时,又会形成一个环,如果两个环不相交,那么答案会继续减小,但是环可能会重合,而对于重合的部分,还是要经过两次。

    所以可以得到k=2的思路:两次DFS求出原数的直径d1,并记录路径。将直径上的所有边权改为-1,表示如果这样走就与第一个环重复,并且在最后答案统计的时候相当于把重合的部分加了回来。用树形DP求树的直径长度d2。

    (注意第一次只能用DFS,因为要记录路径,而第二次只能用树形DP,因为有负边权,距离点u远的点不一定真的远)。

    最终的答案即为2*(n-1)-(d1-1)-(d2-1)。

    注意边权要赋在边上,虽然比较难写:主要在求出d1的路径后,将这个路径上的所有边权改为-1。注意边是双向边,要将两个方向的边权都改成-1。

    AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<queue> 
     4 #include<cstring>
     5 using namespace std;
     6 const int N=100005;
     7 const int INF=2147483647;
     8 int n,k,maxd,tot,head[N],f[N],dis[N],vis[N],p,w[N],ans;
     9 struct node{
    10     int to,next,w;
    11 }edge[N<<1];
    12 void add(int u,int v,int w){
    13     edge[tot].to=v;
    14     edge[tot].next=head[u];
    15     edge[tot].w=w;
    16     head[u]=tot++;
    17 }
    18 void DFS(int u,int fa){
    19     f[u]=fa;
    20     if(maxd<dis[u]){
    21         maxd=dis[u];
    22         p=u;
    23     }
    24     for(int i=head[u];i!=-1;i=edge[i].next){
    25         int v=edge[i].to;
    26         if(vis[v]||v==fa) continue;
    27         vis[v]=1;
    28         dis[v]=dis[u]+edge[i].w;
    29         DFS(v,u);
    30     }
    31 }
    32 void DP(int u){
    33     vis[u]=1;
    34     for(int i=head[u];i!=-1;i=edge[i].next){
    35         int v=edge[i].to;
    36         if(vis[v]) continue;
    37         DP(v);
    38         ans=max(ans,dis[u]+dis[v]+edge[i].w);
    39         dis[u]=max(dis[u],dis[v]+edge[i].w);
    40     }
    41 }
    42 int main(){
    43     memset(head,-1,sizeof(head));
    44     memset(f,-1,sizeof(f));
    45     scanf("%d%d",&n,&k);
    46     for(int i=1;i<=n-1;i++){
    47         int a,b;
    48         scanf("%d%d",&a,&b);
    49         add(a,b,1); add(b,a,1);
    50     }
    51     int a,b,d1,d2;
    52     DFS(1,-1);
    53     a=p;
    54     maxd=0;
    55     memset(dis,0,sizeof(dis));
    56     memset(f,-1,sizeof(f));
    57     memset(vis,0,sizeof(vis));
    58     DFS(p,-1);
    59     b=p;
    60     d1=maxd;
    61     if(k==1){
    62         printf("%d",2*(n-1)-(d1-1));
    63         return 0;
    64     }
    65     for(int i=b;i!=-1;i=f[i]){
    66         for(int j=head[i];j!=-1;j=edge[j].next){
    67             int v=edge[j].to;
    68             if(v==f[i]) edge[j].w=-1;
    69         }
    70         for(int j=head[f[i]];j!=-1;j=edge[j].next){
    71             int v=edge[j].to;
    72             if(v==i) edge[j].w=-1;
    73         }
    74     }
    75     memset(vis,0,sizeof(vis));
    76     memset(dis,0,sizeof(dis));
    77     DP(1);
    78     d2=ans;
    79     printf("%d
    ",2*(n-1)-(d1-1)-(d2-1));
    80     return 0;
    81 }
    AC代码
  • 相关阅读:
    【Unity3D】使用MD5值,确保本地Sqlite数据库内容没有被篡改
    《Unity3D》通过对象池模式,管理场景中的元素
    NGUI制作 《九宫格》 图片
    NGUI混合FingerGesture《卷二》分离触摸事件
    js的各种获取大小
    sass基础
    js面向对象开发基础
    正则表达式(进阶篇)
    正则表达式(基础篇)
    jquery源码学习(三)—— jquery.prototype主要属性和方法
  • 原文地址:https://www.cnblogs.com/New-ljx/p/13828843.html
Copyright © 2020-2023  润新知