BZOJ 3011 Usaco2012 Dec Running Away From the Barn 可并堆

题目大意:给定一棵有根树,求以每个点为根的子树中有多少点到它的距离不超过l

第一眼是可并堆- - 于是怒写- - 管它正解是啥- -

从下到上维护可并大根堆 键值是该点到当前根节点的距离 一旦堆顶剪枝大于l就弹顶

时间复杂度O(nlogn)

什么?你说将整个堆都加上一个值?

打标记不就好了- - 毫无疑问可并堆是可以打标记的- -

此外我的随机堆写if(flag^=1)就T写if(rand()&1)就秒过是什么鬼- -

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;
struct edge{
	int to,next;
	long long f;
}table[M<<1];
int head[M],tot;
int n,ans[M];
long long l;
void Add(int x,int y,long long z)
{
	table[++tot].to=y;
	table[tot].f=z;
	table[tot].next=head[x];
	head[x]=tot;
}
namespace Random_Heap{
	struct abcd{
		abcd *ls,*rs;
		long long val,mark;
		int size;
		abcd():ls(0x0),rs(0x0),val(0),mark(0),size(1) {}
		void Add(long long x)
		{
			val+=x;
			mark+=x;
		}
		void Push_Down()
		{
			if(mark)
			{
				if(ls) ls->Add(mark);
				if(rs) rs->Add(mark);
				mark=0;
			}
		}
		void Push_Up()
		{
			size=1;
			if(ls) size+=ls->size;
			if(rs) size+=rs->size;
		}
	}*heap[M];
	abcd* Merge(abcd *x,abcd *y)
	{
		if(!x) return y;
		if(!y) return x;
		if(x->val<y->val)
			swap(x,y);
		x->Push_Down();
		if(rand()&1)
			x->rs=Merge(x->rs,y);
		else
			x->ls=Merge(x->ls,y);
		x->Push_Up();
		return x;
	}
	void Pop(abcd *&x)
	{
		x->Push_Down();
		x=Merge(x->ls,x->rs);
	}
}
void Tree_DP(int x)
{
	using namespace Random_Heap;
	int i;
	heap[x]=new abcd;
	for(i=head[x];i;i=table[i].next)
	{
		Tree_DP(table[i].to);
		heap[table[i].to]->Add(table[i].f);
		while( heap[table[i].to] && heap[table[i].to]->val>l )
			Pop(heap[table[i].to]);
		heap[x]=Merge(heap[x],heap[table[i].to]);
		
	}
	ans[x]=heap[x]->size;
}
int main()
{
	int i,x;
	long long y;
	cin>>n>>l;
	for(i=2;i<=n;i++)
	{
		scanf("%d%lld",&x,&y);
		Add(x,i,y);
	}
	Tree_DP(1);
	for(i=1;i<=n;i++)
		printf("%d\n",ans[i]);
	return 0;
}


你可能感兴趣的:(bzoj,BZOJ3011,可并堆)