题目大意:给定一棵有根树,求以每个点为根的子树中有多少点到它的距离不超过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 3011 Usaco2012 Dec Running Away From the Barn 可并堆
原文地址:http://blog.csdn.net/popoqqq/article/details/43413379