1. 程式人生 > >POJ 1741 點分治

POJ 1741 點分治

ret oid 框架 樹根 d+ using back fir include

方法:指針掃描數組

每次選擇樹的重心作為樹根,從樹根出發進行一次DFS,求出點到樹根的距離,把節點按照與樹根的的距離放進數組d,設置兩個指針L,R分別從前、後開始掃描,每次滿足條件時答案累加R-L。,之後減去子樹的滿足條件的情況,刪除根節點,對其子樹繼續上述操作,不斷累加答案。

代碼:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=100010;
vector<pair<int,int> >G[maxn];
int d[maxn],size[maxn],root,max_w,n1,cnt,n,k;
bool vis[maxn];
void get_root(int x,int f){//求樹的重心 
	size[x]=1;
	int max_part=0;
	for(int i=0;i<G[x].size();i++){
		int Next=G[x][i].first;
		if(Next!=f&&!vis[Next]){
			get_root(Next,x);
			size[x]+=size[Next];
			max_part=max(max_part,size[Next]);
		}
	}
	max_part=max(max_part,n1-size[x]);
	if(max_part<max_w){
		root=x;
		max_w=max_part;
	}
}
void get_dist(int x,int f,int dist){//算距離 
	d[++cnt]=dist;
	size[x]=1;//算距離的同時也更新一下子樹的大小 
	for(int i=0;i<G[x].size();i++){
		int Next=G[x][i].first;
		if(Next!=f&&!vis[Next]){
			get_dist(Next,x,dist+G[x][i].second);
			size[x]+=size[Next];
		}
	}
}
int cal(int x,int y){//計算 
	cnt=0;
	get_dist(x,-1,y);
	sort(d+1,d+1+cnt);
	int ans=0;
	for(int i=1,j=cnt;i<j;i++){
		while(d[i]+d[j]>k&&i<j)j--;
		ans+=j-i;
	}
	return ans;
}
int dfs(int x){//dfs主框架 
	max_w=n1;
	get_root(x,-1);
	int now=root;
	vis[now]=1;
	int ans=0;
	ans+=cal(now,0);
	for(int i=0;i<G[now].size();i++){
		int Next=G[now][i].first;
		if(!vis[Next]){
			ans-=cal(Next,G[now][i].second);
			n1=size[Next];
			ans+=dfs(Next);
		}
	}
	return ans;
}
void init(int n){
	for(int i=1;i<=n;i++)G[i].clear();
	cnt=0;
	memset(vis,0,sizeof(vis));
}
int main(){
	int u,v,dis;
	while(~scanf("%d%d",&n,&k)&&n&&k){
		init(n);
		for(int i=1;i<n;i++){
			scanf("%d%d%d",&u,&v,&dis);
			G[u].push_back(make_pair(v,dis));
			G[v].push_back(make_pair(u,dis));
		}
		n1=n;
		printf("%d\n",dfs(1));
	}
}
//5 1
//1 2 1
//2 3 1
//3 4 1
//4 5 1

POJ 1741 點分治