算法复习——(树)点分治

题目:

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\n",ans);
  }
  return 0;
}

 

 

posted @ 2017-03-29 19:30  AseanA  阅读(228)  评论(0编辑  收藏  举报