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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章