首页 > 代码库 > 【POJ 1741】 Tree (树的点分治)

【POJ 1741】 Tree (树的点分治)

Tree
 

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

Source

LouTiancheng@POJ
 
 
【题意】
  求树上两点间距离小等于K的方案数 (n<=10000)
 
【分析】
  经典的点分治问题。
  搞笑的我知道怎么做都纠集好久,纠结症怎么破。
  每层求重心方法分治是nlogn的。
  具体是,每次都只算lca为根的点对。
  设dis[x]为x节点到根的距离,那么我们求dis[x]+dis[y]<=k的方案数,并且x和y要在同子树上。
  先不考虑在不同子树上,直接求dis[x]+dis[y]<=k可以排序一次然后for一遍动态累加,然后再把同一棵子树上的方案数剪掉。
 
代码如下:
技术分享
  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<cmath>
  8 using namespace std;
  9 #define Maxn 10010
 10 #define INF 0xfffffff
 11 
 12 struct node
 13 {
 14     int x,y,c,next;
 15 }t[Maxn*2];int len;
 16 int first[Maxn];
 17 int n,k;
 18 
 19 int mymax(int x,int y) {return x>y?x:y;}
 20 
 21 void ins(int x,int y,int c)
 22 {
 23     t[++len].x=x;t[len].y=y;t[len].c=c;
 24     t[len].next=first[x];first[x]=len;
 25 }
 26 int rt;
 27 bool q[Maxn];
 28 int sm[Maxn],mx[Maxn],dep[Maxn];
 29 int v[Maxn],vl;
 30 
 31 void dfs(int x,int h,int f)
 32 {
 33     mx[x]=-1;sm[x]=1;
 34     for(int i=first[x];i;i=t[i].next) if(t[i].y!=f&&q[t[i].y])
 35     {
 36         int y=t[i].y;//dep[y]=dep[x]+t[i].c;
 37         dfs(y,h,x);
 38         sm[x]+=sm[y];
 39         mx[x]=mymax(mx[x],sm[y]);
 40     }
 41     mx[x]=mymax(mx[x],h-sm[x]);
 42     if(mx[x]<mx[rt]) rt=x;
 43 }
 44 
 45 int get_ans()
 46 {
 47     int now=1,ans=0;
 48     sort(v+1,v+1+vl);
 49     for(int i=vl;i>=1;i--)
 50     {
 51         if(now>i) now=i;
 52         while(v[i]+v[now]<=k&&now<i) now++;
 53         ans+=now-1;
 54     }
 55     return ans;
 56 }
 57 
 58 void dfs2(int x,int f)
 59 {
 60     v[++vl]=dep[x];
 61     for(int i=first[x];i;i=t[i].next) if(t[i].y!=f&&q[t[i].y])
 62     {
 63         int y=t[i].y;
 64         dep[y]=dep[x]+t[i].c;
 65         dfs2(y,x);
 66     }
 67 }
 68 
 69 int fans;
 70 
 71 void ffind(int x,int f)
 72 {
 73     vl=0;dep[x]=0;dfs2(x,0);
 74     fans+=get_ans();
 75     q[x]=0;
 76     for(int i=first[x];i;i=t[i].next) if(t[i].y!=f&&q[t[i].y])
 77     {
 78         int y=t[i].y;
 79         vl=0;dfs2(y,x);
 80         fans-=get_ans();
 81     }int i;
 82     for(i=first[x];i;i=t[i].next) 
 83         if(t[i].y!=f&&q[t[i].y])
 84     {
 85         rt=0;
 86         dfs(t[i].y,x,sm[t[i].y]);
 87         ffind(rt,x);
 88     }
 89 }
 90 
 91 int main()
 92 {
 93     while(1)
 94     {
 95         scanf("%d%d",&n,&k);
 96         if(n==0&&k==0) break;
 97         memset(first,0,sizeof(first));
 98         len=0;
 99         for(int i=1;i<n;i++)
100         {
101             int x,y,c;
102             scanf("%d%d%d",&x,&y,&c);
103             ins(x,y,c);ins(y,x,c);
104         }
105         fans=0;
106         memset(q,1,sizeof(q));
107         mx[0]=INF;
108         rt=0;vl=0;dfs(1,n,0);
109         ffind(rt,0);
110         printf("%d\n",fans);
111     }
112     return 0;
113 }
[POJ 1741]

其实不知道我打的点分治标不标准,感觉很丑。这题就有3个dfs,ffind是求分治的子树答案,dfs是求子树重心,dfs2是求dis以及把子树的dis加入到v中排序。

 

2016-10-16 22:16:44

【POJ 1741】 Tree (树的点分治)