zoukankan      html  css  js  c++  java
  • 【DFS好题】BZOJ1999- [Noip2007]Core树网的核(数据加强版)

    NOIP的数据好水,一开始有好几个错结果NOIP数据就水过了??

    【题目大意】

    求无根树的直径上一段不超过S长的链,使得偏心距最小。具体概念见原题

    【思路】

    首先明确几个性质:

    (1)对于树中的任意一点,距离其最远的点一定是树的直径的某一端点。

    (2)所有的直径是等价的,即任意一条所能求出的该最小偏心距相等。

    于是我们可以用两次dfs求出直径。任取一个点找到离它最远的点r,再从r找到距离它最远的点l。l到r的路径就是直径。

    显然在长度不超过S的情况下,链最长最好。在l到r上维护尽可能长的链,找到左右端点到直径做右端点的较大值的最小值。然后由链上各个点出发,找到不经过直径上的点抵达的其他点的最大深度。这个最大深度和之前的最小值中较大的就是答案。

    为什么是整条直径上找最大深度,而不是在核上找呢?

    显然,如果这个深度最深的点不是从核中的点,那么它到核的距离必定小于核的端点到直径端点的距离。所以如果有个节点到核的距离小于核的端点到直径端点的距离,那么它必定是从核上延伸出去的。

    【错误点】

    具体见程序。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int MAXN=500000+50;
     4 const int INF=0x7fffffff;
     5 struct edge
     6 {
     7     int to,len;
     8 };
     9 vector<edge> E[MAXN];
    10 int n,s;
    11 int l,r,dis[MAXN],f[MAXN],ban[MAXN];
    12  
    13 void addedge(int u,int v,int w)
    14 {
    15     E[u].push_back((edge){v,w});
    16     E[v].push_back((edge){u,w});
    17 }
    18  
    19 void init()
    20 {
    21     scanf("%d%d",&n,&s);
    22     for (int i=1;i<n;i++)
    23     {
    24         int u,v,w;
    25         scanf("%d%d%d",&u,&v,&w);
    26         addedge(u,v,w);
    27     }
    28 }
    29  
    30 void dfs(int u,int fa)
    31 {
    32     f[u]=fa;
    33     for (int i=0;i<E[u].size();i++)
    34     {
    35         int to=E[u][i].to;
    36         if (ban[to] || to==f[u]) continue;
    37         dis[to]=dis[u]+E[u][i].len;
    38         dfs(to,u);
    39     }
    40 }
    41  
    42 void getd()
    43 {
    44     memset(ban,0,sizeof(ban));
    45      
    46     l=1,r=1;
    47     dfs(l,0);
    48     for (int i=1;i<=n;i++) if (dis[i]>dis[r]) r=i;
    49      
    50     l=r;
    51     dis[r]=0;
    52     dfs(r,0);
    53     for (int i=1;i<=n;i++) if (dis[i]>dis[l]) l=i;
    54 }
    55  
    56 void solve()
    57 {
    58     int i=l,j=l,ans=INF;
    59     for (;i;i=f[i])
    60     {
    61         while (f[j] && dis[i]-dis[f[j]]<=s) j=f[j];
    62         ans=min(ans,max(dis[j],dis[l]-dis[i]));
    63         //每次找到以i为一个端点的最接近于S的链
    64         //比较两端点和直径端点的长度 
    65     }
    66     for (i=l;i;i=f[i]) ban[i]=1;
    67     //由于要找出不经过直径的最大深度,所以禁止访问直径上的点 
    68     for (int i=l;i;i=f[i]) dis[i]=0,dfs(i,f[i]);
    69     //★★★★★★★注意这里i的父亲必须传进去f[i],否则就修改了直径 
    70     for (int i=1;i<=n;i++) ans=max(ans,dis[i]);
    71     printf("%d",ans);
    72 }
    73  
    74 int main()
    75 {
    76     init();
    77     getd();
    78     solve();
    79     return 0;
    80 } 
  • 相关阅读:
    linux下安装vsftp
    CentOS 7.0编译安装Nginx1.6.0+MySQL5.6.19+PHP5.5.14
    Centos编译安装PHP 5.5笔记
    centos使用更新更快的yum源
    使用Android Studio搭建Android集成开发环境
    设计模式(三)原型模式
    overridePendingTransition
    Android手势密码实现
    UserInfoActivity用户图像修改和退出登录
    设计模式(二)建造者模式
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/6062813.html
Copyright © 2011-2022 走看看