zoukankan      html  css  js  c++  java
  • 算法复习——(树)点分治

    题目:

    Description

    Give a tree with n vertices,each edge has a length(positive integer less than 1001). 
    Define dist(u,v)=The min distance between node u and v. 
    Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k. 
    Write a program that will count how many pairs which are valid for a given tree. 

    Input

    The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l. 
    The last test case is followed by two zeros. 

    Output

    For each test case output the answer on a single line.

    Sample Input

    5 4
    1 2 3
    1 3 1
    1 4 2
    3 5 1
    0 0
    

    Sample Output

    8

    题解:

    PS:在此引用神犇sdj222555的题解,orz···附上链接:http://blog.csdn.net/sdj222555/article/details/7893862

    题意就是求树上距离小于等于K的点对有多少个

    n2的算法肯定不行,因为1W个点

    这就需要分治。可以看09年漆子超的论文

    本题用到的是关于点的分治。

    一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。

    每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心

    找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。

    最后的复杂度是n logn logn    其中每次快排是nlogn 而递归的深度为logn

    心得:

    用一句话来总结点分治吧···每次求树的重心,求出经过这个重心的符合条件的路径后将这个点连边删除···

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<string>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=10005;
    int read()
    {
      char c;
      int i=1,f=0;
      for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar());
      if(c=='-')
      {
        i=-1;
        c=getchar();
      }
      for(;c>='0'&&c<='9';c=getchar())
        f=(f<<3)+(f<<1)+c-'0';
      return f*i; 
    }
    int tot,first[N],next[N*5],go[N*5],val[N*5];
    int n,k,gravcent,minn;
    int size[N],maxx[N],dis[N];
    bool check[N];
    int cnt=0,ans;
    inline void clear()
    {
      tot=ans=0;
      memset(first,0,sizeof(first));
      memset(check,false,sizeof(check));
      memset(next,0,sizeof(next));
      memset(maxx,0,sizeof(maxx));
    }
     
    inline void comb(int u,int v,int w)
    {
      next[++tot]=first[u],first[u]=tot,go[tot]=v,val[tot]=w;
      next[++tot]=first[v],first[v]=tot,go[tot]=u,val[tot]=w;
    }
    
    inline void dfssize(int u,int last)
    {
      size[u]=1;
      maxx[u]=0;
      for(int e=first[u];e;e=next[e])
      {
        int v;
        if((v=go[e])==last||check[v]==true)  continue;
        dfssize(v,u);
        size[u]+=size[v];
        if(size[v]>maxx[u])  maxx[u]=size[v];
      }
    }
    
    inline void dfsgravcent(int root,int u,int last)
    { 
      if(size[root]-size[u]>maxx[u])  maxx[u]=size[root]-size[u];
      if(maxx[u]<minn) minn=maxx[u],gravcent=u;
      for(int e=first[u];e;e=next[e])
      {
        int v=go[e];
        if(v==last||check[v]==true)  continue;
        dfsgravcent(root,v,u);
      }
    }
    
    inline void dfsdis(int u,int d,int last)
    {
      dis[++cnt]=d;
      for(int e=first[u];e;e=next[e])
      {
        int v=go[e];
        if(v==last||check[v]==true)  continue;
        dfsdis(v,d+val[e],u);  
      }
    }
    
    inline int calc(int u,int d)
    {
      int temp=0;
      cnt=0;
      dfsdis(u,d,0);
      sort(dis+1,dis+cnt+1);
      int i=1,j=cnt;
      while(i<j)
      {
        while(i<j&&dis[i]+dis[j]>k)  j--;
        temp+=j-i;
        i++;
      }
      return temp;
    }
    inline void dfs(int u)
    {
      minn=n;
      dfssize(u,0);
      dfsgravcent(u,u,0);
      ans+=calc(gravcent,0);
      check[gravcent]=true;
      for(int e=first[gravcent];e;e=next[e])
      {
        int v=go[e];
        if(!check[v])
        {
          ans-=calc(v,val[e]);
          dfs(v);
        }
      }
    }
    int main()
    {
     // freopen("a.in","r",stdin);
      while(true)
      {  
        n=read();
        k=read();
        if(n==0&&k==0)  break;
        clear();
        for(int i=1;i<n;i++)
        {
          int u,v,w;
          u=read();
          v=read();
          w=read();
          comb(u,v,w);
        }
        dfs(1);
        printf("%d
    ",ans);
      }
      return 0;
    }
    
     
  • 相关阅读:
    最大子串和
    [USACO1.5]数字金字塔 Number Triangles
    数字金字塔
    台阶问题
    取余运算
    数列分段pascal程序
    Java 集合-Collection接口和迭代器的实现
    Java 集合-集合介绍
    Java IO流-File类
    Git学习记录
  • 原文地址:https://www.cnblogs.com/AseanA/p/6641370.html
Copyright © 2011-2022 走看看