poj1741 點分治

題意:求一顆節點數爲n(n<=10000)的樹中,距離不超過k的點對(x,y)(x<y)的數量。

解:考慮點分治

        分情況討論,過根節點的鏈直接dfs+掃一遍統計方案數,方案數計算方法爲:將該樹所有離根節點的距離存下來排序,然後統計個數,再減去在同一子樹內的點對數。

            不過根節點的鏈通過遞歸子樹求出,重複上面步驟。

            爲了防止子樹退化成鏈從而退化爲O(n^2),所以我們對於每一顆子樹求出其重心再進行計算。

             (重心是使該樹最大子樹最小的節點)

代碼(204ms):

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define uint unsigned int
#define For(i,j,k) for(register uint i=(j);i<=(uint)k;i++) 
#define Forr(i,j,k) for(register uint i=(j);i>=(uint)k;i--)
#define Rep(i,u) for(register uint i=Begin[u],v=to[i];i;i=Next[i],v=to[i])
#define Set(a,b) memset((a),b,sizeof(a))
#define ull unsigned long long
#define ll long long
using namespace std;
const int N=10010,M=20010;
int Begin[N],Next[M],to[M],e=1,w[M],p[M],siz[N],dis[N],fa[N];
int n,k,ans;
inline void read(int &x){
	x=0;char c=getchar();int f=0;
	while(c<'0' || c>'9'){c=getchar();if (c=='-')f=1;}
	while(c>='0' && c<='9')x=x*10+c-'0',c=getchar();if (f)x=-x;
}
inline void add(int x,int y,int z){
	to[++e]=y,Next[e]=Begin[x],Begin[x]=e,w[e]=z,p[e]=0;
}
inline void init(){
	Set(Begin,0),e=1,ans=0;
}
void Getcg(int u,int sz,int &cg){
	uint flag(1);
	siz[u]=1;
	Rep(i,u)
		if(!p[i]&&fa[u]!=v){
			fa[v]=u,
			Getcg(v,sz,cg),siz[u]+=siz[v];
			if (siz[v]>sz>>1)flag=0;
		}
	if(sz-siz[u]>sz>>1)flag=0;
	if(flag)cg=u;
}//找出該子樹的重心 
void dfs1(int u,int d){
	dis[u]=d;
	Rep(i,u)
		if (!p[i]&&fa[u]!=v)
			fa[v]=u,dfs1(v,(dis[v]=d+w[i]));
}//求出每個節點到根節點的距離和每個節點的父親 
int top,ret,s[N];
void dfs2(int u){
	s[++top]=dis[u];
	Rep(i,u)
		if (!p[i]&&fa[u]!=v)
			dfs2(v);
}//將所有點離根節點的距離入棧 
int calc(int u){
	top=ret=0;
	dfs2(u);
	sort(s+1,s+top+1);
	for(int i=1,j=top;i<=top;i++){
		for(;j&&s[i]+s[j]>k;j--);
		ret+=j;
	}
	return ret;
}//計算距離小於k的點對個數 
void Tree_Divide(int rt,int sz){
	int cg;
	fa[rt]=0;
	Getcg(rt,sz,cg);
	siz[fa[cg]]=sz-siz[cg];
	fa[cg]=0;
	dfs1(cg,0); 
	ans+=calc(cg);
	Rep(i,cg)
		if (!p[i])ans-=calc(v);
	Rep(i,cg)
		if (!p[i])p[i]=p[i^1]=1,Tree_Divide(v,siz[v]); 
}//點分治求解 
int main(){
	while(scanf("%d%d",&n,&k)&&(n||k)){
		int x,y,z;
		init(); 
		For(i,1,n-1)
			read(x),read(y),read(z),add(x,y,z),add(y,x,z);
		Tree_Divide(1,n);
		printf("%d\n",(ans-n)/2); 
	}
	return 0;
}


發佈了32 篇原創文章 · 獲贊 0 · 訪問量 6945
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章