【BZOJ3011】[Usaco2012 Dec]Running Away From the Barn【可並堆】

【題目鏈接】

pkusc時候做的題,現在發上來。


每個節點維護一個大根堆,維護從根節點到當前節點以及當前節點的子節點的路徑長度(即深度)。

如果當前節點的堆的堆頂 - 當前節點的深度 大於L,那麼刪除堆頂,直到滿足條件爲止。

那麼堆的size就是當前節點的答案。

當一個節點處理完畢後,把當前節點的堆與當前節點的父親的堆合併,就可以了。

當一個節點的兒子都處理完後,那麼就可以處理這個節點了,所以用類似拓撲序的思想,用一個隊列搞。


可並堆用的左偏樹。

/* Forgive me Not */
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long LL;

const int maxn = 200005;

int n, pre[maxn], size[maxn], ans[maxn], du[maxn], son[maxn][2], root[maxn];
LL L, w[maxn];
queue<int> q;

int dis[maxn];

inline void pushup(int x) {
	size[x] = size[son[x][0]] + size[son[x][1]] + 1;
}

inline int merge(int x, int y) {
	if(!x || !y) return x + y;
	if(w[x] < w[y]) swap(x, y);
	son[x][1] = merge(son[x][1], y);
	pushup(x);
	if(dis[son[x][0]] < dis[son[x][1]]) swap(son[x][0], son[x][1]);
	dis[x] = dis[son[x][1]] + 1;
	return x;
}

int main() {
	scanf("%d%lld", &n, &L);
	size[1] = 1; root[1] = 1; du[0] = -1;
	for(int i = 2; i <= n; i++) {
		scanf("%d%lld", &pre[i], &w[i]);
		w[i] += w[pre[i]];
		size[i] = 1;
		root[i] = i;
		du[pre[i]]++;
	}
	for(int i = 1; i <= n; i++) if(!du[i]) q.push(i);
	while(!q.empty()) {
		int u = q.front(); q.pop();
		for(; w[root[u]] - w[u] > L; root[u] = merge(son[root[u]][0], son[root[u]][1]));
		ans[u] = size[root[u]];
		root[pre[u]] = merge(root[pre[u]], root[u]);
		du[pre[u]]--;
		if(!du[pre[u]]) q.push(pre[u]);
	}
	for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
	return 0;
}


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