zoukankan      html  css  js  c++  java
  • 【NOI2003T4】逃学的小孩-贪心+树形DP求直径+倍增LCA

    测试地址:逃学的小孩

    做法:通过各种证明可以得到一个贪心的思路:当A,B分别为树直径的两端时,一定存在最优答案。求直径我们用树形DP可以O(n)求出:求出以某一个点为根的子树上与其距离最远和次远的点及根与它们的距离,而且要保证根到这两点的路径不相交,这样就可以求出通过某点的最长路径,然后再枚举每一个点求最大值就是树中的最长路径,也就是直径。接下来枚举C,则答案为max(dis(A,B)+min(dis(A,C),dis(B,C))),求树上两点间的距离可以用倍增LCA做,时间复杂度是O(nlogn)的。注意一些值可能大于32位整数所能存下的最大值,所以要使用long long型存储。

    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    int mxp[200010],smxp[200010],dep[200010],tot=0;
    ll mx[200010],smx[200010],dd[200010],dis,ans;
    int n,m,fa[200010][20],first[200010]={0};
    struct edge {int v,next;ll d;} e[400010];
    
    void insert(int a,int b,int d)
    {
      e[++tot].v=b,e[tot].d=d,e[tot].next=first[a],first[a]=tot;
    }
    
    void dfs(int v)
    {
      mx[v]=smx[v]=0,mxp[v]=smxp[v]=v;
      for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=fa[v][0])
    	{
    	  fa[e[i].v][0]=v;
    	  dep[e[i].v]=dep[v]+1;
    	  dd[e[i].v]=dd[v]+e[i].d;
    	  dfs(e[i].v);
    	  if (mx[e[i].v]+e[i].d>mx[v])
    	  {
    	    smx[v]=mx[v],smxp[v]=mxp[v];
    		mx[v]=mx[e[i].v]+e[i].d,mxp[v]=mxp[e[i].v];
    	  }
    	  else if (mx[e[i].v]+e[i].d>smx[v])
    	  {
    	    smx[v]=mx[e[i].v]+e[i].d,smxp[v]=mxp[e[i].v];
    	  }
    	}
    }
    
    int lca(int x,int y)
    {
      if (dep[x]<dep[y]) swap(x,y);
      for(int i=19;i>=0;i--)
      {
        if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
      }
      if (x==y) return x;
      for(int i=19;i>=0;i--)
      {
        if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
      }
      return fa[x][0];
    }
    
    ll calc_dis(int x,int y)
    {
      int f=lca(x,y);
      return dd[x]+dd[y]-2*dd[f];
    }
    
    int main()
    {
      scanf("%d%d",&n,&m);
      for(int i=1,a,b,d;i<n;i++)
      {
        scanf("%d%d%d",&a,&b,&d);
        insert(a,b,d),insert(b,a,d);
      }
      
      dep[1]=dd[1]=0;
      dfs(1);
      mx[0]=smx[0]=0;
      int mxi=0;
      for(int i=1;i<=n;i++)
        if (mx[i]+smx[i]>mx[mxi]+smx[mxi]) mxi=i;
      
      for(int i=1;i<=19;i++)
        for(int j=1;j<=n;j++)
    	  fa[j][i]=fa[fa[j][i-1]][i-1];
      dis=calc_dis(mxp[mxi],smxp[mxi]);
      
      ans=0;
      for(int i=1;i<=n;i++)
        if (i!=mxp[mxi]&&i!=smxp[mxi])
    	  ans=max(ans,min(calc_dis(mxp[mxi],i),calc_dis(smxp[mxi],i)));
      
      printf("%lld",dis+ans);
      return 0;
    }
    


  • 相关阅读:
    WINDOWS操作系统下OpenERP源码运行的环境:eclipse + pydev + python2.7
    12款优秀的 Twitter Bootstrap 组件和工具
    推荐电脑上好用的但相对冷门的软件
    财务记账演练
    OpenERP Custom Sample Module Development – OpenERP Quick Start Guide
    免费编程入门教程资源推荐搜集
    50个很棒的Python模块
    kindle 资源
    怎么看内存的类型?
    计算机必懂的53个英文单词和缩写
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793780.html
Copyright © 2011-2022 走看看