码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj-3011 Running Away From the Barn

时间:2015-08-25 23:48:42      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:bzoj   可并堆   

题意:

给出以1号点为根的一棵有根树,问每个点的子树中与它距离小于等于L的点有多少个。

n<=200000;


题解:

这题比较有意思;

首先考虑的就是树形DP,但是DP完全无法转移;

考虑一个结点的答案,那就是子树中与这个结点距离小于等于L的点数(废话);

那它父亲在这个子树的答案呢?

子树中每个点的距离都增加了,而相对大小关系没有改变;

所以就用一个可并堆来维护子树,每次将堆中距离过大的点pop掉;

堆中长度直接用这个点到1的长度,而到当前子树根的距离在减去子树根到1的长度就是了;

然后统计堆的大小,再将堆向上合并;

时间复杂度是O(nlogn),扫一遍树就出解了;

如果边权带负怎么办?

每个点可能加入堆中多次,但是如果搞一个对顶堆复杂度就不对了;

所以上启发式合并平衡树= =,O(nlog^2n)解决了;


代码:


#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 210000
using namespace std;
typedef long long ll;
int ch[N][2],dis[N],size[N];
int out[N],fa[N],root[N],ans[N];
ll L[N];
queue<int>q;
void Pushup(int x)
{
	size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
int merge(int x,int y)
{
	if(!x||!y)	return x+y;
	if(L[x]<L[y])
		swap(x,y);
	ch[x][1]=merge(ch[x][1],y);
	Pushup(x);
	if(dis[ch[x][0]]<dis[ch[x][1]])
		swap(ch[x][0],ch[x][1]);
	dis[x]=dis[ch[x][1]]+1;
	return x;
}
int main()
{
	int n,i,j,k,x,y;
	ll m;
	scanf("%d%lld",&n,&m);
	dis[0]=-1,size[1]=1,root[1]=1;
	for(i=2;i<=n;i++)
	{
		scanf("%d%lld",fa+i,L+i);
		out[fa[i]]++,size[i]=1,root[i]=i;
		L[i]+=L[fa[i]];
	}
	for(i=1;i<=n;i++)
	{
		if(out[i]==0)
			q.push(i);
	}
	out[0]++;
	while(!q.empty())
	{
		x=q.front(),q.pop();
		while(L[root[x]]-L[x]>m)
			root[x]=merge(ch[root[x]][0],ch[root[x]][1]);
		ans[x]=size[root[x]];
		y=fa[x];
		root[y]=merge(root[y],root[x]);
		out[y]--;
		if(!out[y])
			q.push(y);
	}
	for(i=1;i<=n;i++)
		printf("%d\n",ans[i]);
	return 0;
}



bzoj-3011 Running Away From the Barn

标签:bzoj   可并堆   

原文地址:http://blog.csdn.net/ww140142/article/details/47984709

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!