poj2114(树的点分治)

    参考文章: http://blog.csdn.net/yang_7_46/article/details/9966455

  

   题目的大致意思是要求出是否存在两点间的路径长度为K的情况

  不知道为什么1741一直是re,主要还是看别人文章学习来的树的点分治。

  

  点分治,主要是分类来进行处理,对于两点之前求距离可以分为两类情况:

  1.过根节点(不在一棵子树内)

  2.不过根节点(在同一棵子树内)


  第2种情况可以通过递归转化成第1种情况,对于第1种情况,求出到根节点的距离dis,然后它们之前的距离就为dis[i] + dis[j],对于求满足的情况,有个比较巧妙的办法,就是对其排序之后,然后在O(n)的时间内找到,主要还是在对1741的时候作用比较大(1741是要求距离小于等于K,2114是要求刚好等于K)


  另外对于树而言,要求其重心(树的最大深度的值最小),这样在计算的时候可以避免出现类似一条链的情况。 

//得到重心
void getRoot(int now, int fa)
{
	//s代表树中点的个数,f代表它的深度
	s[now] = 1; f[now] = 0;
	for(int k = head[now]; k != -1; k = edge[k].next){
		//如果访问过,或者是其父节点
		if( fa == edge[k].t || visit[edge[k].t]) continue;
		getRoot(edge[k].t, now);
		s[now] += s[edge[k].t];
		f[now] = max(f[now], s[edge[k].t]);
	}

	f[now] = max(f[now], size - s[now]);
	if( f[now] < f[root] )
		root = now;

}
<pre name="code" class="cpp">//得到距离根的距离
void getD(int now, int fa)
{
	dep.push_back(d[now]);
	s[now] = 1;
	for(int k = head[now]; k != -1; k = edge[k].next){
		if( edge[k].t == fa || visit[edge[k].t] ) continue;
		d[edge[k].t] = d[now] + edge[k].w;
		getD(edge[k].t, now);
		s[now] += s[edge[k].t];
	}
}

//计算个数
int calc(int now, int dis)
{
	dep.clear();
	d[now] = dis;
	getD(now,0);

	sort(dep.begin(), dep.end());

	int ret = 0, l = 0, r = dep.size() - 1;

	while( l < r ){
		if (dep[l] + dep[r] == K) {  
			if (dep[l] == dep[r]) {  
				ret += (r-l+1)*(r-l)/2; break;  
			}  
			int i=l, j=r;  
			while (dep[i] == dep[l]) i++;  
			while (dep[j] == dep[r]) j--;  
			ret += (i-l)*(r-j);  
			l = i, r = j;  
		} else if (dep[l] + dep[r] < K) l++;  
		else r--; 
	}

	return ret;
}



void work(int temp)
{
	ans += calc(temp,0);
	visit[temp] = true;

	for(int k = head[temp]; k != -1; k = edge[k].next){
		if( visit[edge[k].t] ) continue;
		ans -= calc(edge[k].t, edge[k].w);
		f[0] = size = s[edge[k].t];
		getRoot(edge[k].t, root = 0);
		work(root);
	}
}


void solve()
{
	Mem(head, -1);

	int u,w;

	kNum = 0;

	for(int i = 1; i <= n; i ++){
		while(cin>>u && u){
			cin>>w;
			addEdge(i, u, w);
			addEdge(u, i, w);
		}
		
	}

	while(cin>>K && K){
		Mem(visit,false);
		f[0] = size = n;
		getRoot(1, root = 0);
		ans = 0;
		work(root);
		printf("%s\n",ans > 0 ? "AYE" : "NAY" );
	}
	printf(".\n");
}


 



  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值