POJ1741 Tree

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

 

 

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


正解:树的点分治
解题报告:
  漆子超的论文题,树的点分治模板题。
  点分治的题目惯用思路都是考虑路径过根的情况然后子树内分类讨论即可,我们考虑讨论如何处理这道题。显然每次找重心,则最多做$logn$次。首先$dfs$一遍处理出根节点到所有节点距离,排序之后显然可以直接单调性扫一遍统计答案。那么这样计算的话就会导致同一棵子树的可能会重复计数,那么我再把处在同一个子树内的满足要求的减掉即可。注意统计的方式!我是在算到根节点的时候,同时减去每个儿子节点内的点重复计算的部分,而不是根据到儿子节点的距离来计算!总复杂度就是O(nlognlogn)

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 100011;
const int MAXM = 200011;
int n,k,ecnt,first[MAXN],to[MAXM],next[MAXM],w[MAXM];
int q1[MAXN],q2[MAXN],cnt1,cnt2;//分别存储子树内所有节点,到当前的根节点或者根节点的儿子节点的距离
bool use[MAXN];//当前节点是否已经作为根节点处理过
int size[MAXN],tot,root,mins,father[MAXN];//每个点的子树中节点个数,和当前处理的树内的结点总数
LL ans;
inline void link(int x,int y,int z){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; }
inline void dfs2(int x,int fa){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa || use[v]) continue; father[v]=x; dfs2(v,x); }}
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline void init(){ 
	ecnt=0; memset(first,0,sizeof(first)); ans=0; 
	memset(use,0,sizeof(use)); memset(size,0,sizeof(size)); memset(father,0,sizeof(father)); 
}

inline void dfs(int x,int fa){
	tot++; size[x]=1;
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa || use[v]) continue;
		dfs(v,x); size[x]+=size[v];
	}
}

inline void dp(int x,int fa){
	int nows=tot-size[x];
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa || use[v]) continue;
		nows=max(size[v],nows); dp(v,x);
	}
	if(nows<mins) { mins=nows; root=x; }
}

inline int findheart(int rt){//寻找重心
	tot=0; dfs(rt,0); if(tot==1) return -1;
	mins=(1<<30); root=-1;
	dp(rt,0); return root;
}

inline void DFS(int x,int fa,int D){//得到每个节点的距离
	q2[++cnt2]=D;
	q1[++cnt1]=D;
	for(int i=first[x];i;i=next[i]) { 
		int v=to[i]; if(v==father[x] || use[v]) continue;
		DFS(v,x,D+w[i]);
	}
}

inline int calc(int *q,int N){
	sort(q+1,q+N+1);
	int l=1,r=N; int S=0;
	while(l<r) {
		while(l<r && q[l]+q[r]>k) r--; if(l>=r) break;
		S+=r-l; l++;
	}
	return S;
}

inline void solve(int rt){
	//这里本来不用清空数组的,不然会TLE...
	rt=findheart(rt); if(rt==-1) return ;
	dfs2(rt,0);	father[rt]=0; cnt1=0; q1[++cnt1]=0;

	//先将子树内的贡献减掉
	for(int i=first[rt];i;i=next[i]) {
		int v=to[i]; if(use[v]) continue; 
		cnt2=0; DFS(v,rt,w[i]);	ans-=calc(q2,cnt2); 
	}

	//再把整棵树的情况加入即可
	ans+=calc(q1,cnt1);
	
	use[rt]=1;//标记为访问过
	for(int i=first[rt];i;i=next[i]) { 
		int v=to[i]; if(use[v]) continue;
		solve(v); 
	}
}

inline void work(){   
	while(1) {
		n=getint(); k=getint(); if(n==0 && k==0) break; int x,y,z;
		init(); for(int i=1;i<n;i++) { x=getint(); y=getint(); z=getint(); link(x,y,z); link(y,x,z); }
		solve(1);
		printf("%lld
",ans);
	}
}

int main()
{
    work();
    return 0;
}

  


  

原文地址:https://www.cnblogs.com/ljh2000-jump/p/6345031.html