1. 程式人生 > >HDU 6162 樹鏈剖分

HDU 6162 樹鏈剖分

its 一個 += ++ ons match 思路 eof query

題意:給你一顆樹,每個節點有有一個權值,每次詢問從x到y的最短路上權值在c到d之間的所有的點的權值和是多少。

思路:肯定要用樹剖,因為詢問c到d之間這種操作樹上倍增很難做,但是用其它數據結構可以比較好的查詢。我們可以用線段樹來進行這種操作。每次詢問一個區間時,如果當前區間被查詢區間完全覆蓋,並且區間裏的最大指小於等於d,最小值大於等於c,才返回,否則繼續查詢。這種做法其實可以被卡掉,比如很長的路徑上點權都是1, 2, 1, 2這種,而詢問的c和d都是1,這樣線段樹上的詢問會被卡成O(n)的。我感覺比較可行的做法是離散化之後用主席樹,這樣可以保證O(logn),但是既然沒卡這個,就懶得寫這種做法了。

線段樹做法:

#include <bits/stdc++.h>
#define LL long long
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
using namespace std;
const int maxn = 100010;
int head[maxn], Next[maxn * 2], ver[maxn * 2];
int son[maxn], d[maxn], f[maxn], top[maxn], sz[maxn], dfn[maxn];
int n, m, tot, cnt;
int a[maxn], b[maxn];
void add(int x, int y) {
	ver[++tot] = y;
	Next[tot] = head[x];
	head[x] = tot;
}
struct SegmentTree {
	int mx, mi;
	LL sum;
};
SegmentTree tr[maxn * 4];
void pushup(int o) {
	tr[o].sum = tr[ls(o)].sum + tr[rs(o)].sum;
	tr[o].mi = min(tr[ls(o)].mi, tr[rs(o)].mi);
	tr[o].mx = max(tr[ls(o)].mx, tr[rs(o)].mx);
}
void build(int o, int l, int r) {
	if(l == r) {
		tr[o].mx = tr[o].mi = tr[o].sum = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(ls(o), l, mid);
	build(rs(o), mid + 1, r);
	pushup(o);
}
bool match(int o, int l, int r) {
	return tr[o].mi >= l && tr[o].mx <= r;
}
LL query(int o, int l, int r, int ql, int qr, int lb, int rb) {
	if(l >= ql && r <= qr && match(o, lb, rb)) {
		return tr[o].sum;
	}
	int mid = (l + r) >> 1;
	LL ans = 0;
	if(ql <= mid && !(tr[ls(o)].mx < lb || tr[ls(o)].mi > rb)) ans += query(ls(o), l, mid, ql, qr, lb, rb);
	if(qr > mid && !(tr[rs(o)].mx < lb || tr[rs(o)].mi > rb)) ans += query(rs(o), mid + 1, r, ql, qr, lb, rb);
	return ans;
}
void dfs1(int x, int fa) {
	sz[x] = 1;
	f[x] = fa;
	d[x] = d[fa] + 1;
	for (int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == fa) continue;
		dfs1(y, x);
		sz[x] += sz[y];
		if(!son[x] || sz[y] > sz[son[x]])
			son[x] = y;
	}
}
void dfs2(int x, int fa, int t) {
	top[x] = t;
	dfn[x] = ++cnt;
	a[dfn[x]] = b[x];
	if(son[x]) dfs2(son[x], x, t);
	for (int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == fa || y == son[x]) continue;
			dfs2(y, x, y);
	}
}
LL solve(int l, int r, int x, int y) {
	LL ans = 0;
	while(top[l] != top[r]) {
		if(d[top[l]] > d[top[r]]) {
			ans += query(1, 1, n, dfn[top[l]], dfn[l], x, y);
			l = f[top[l]]; 
		} else {
			ans += query(1, 1, n, dfn[top[r]], dfn[r], x, y);
			r = f[top[r]];
		}
	}
	if(d[l] < d[r]) ans += query(1, 1, n, dfn[l], dfn[r], x, y);
	else ans += query(1, 1, n, dfn[r], dfn[l], x, y);
	return ans;
}
int main() {
	int x, y, l, r;
	while(~scanf("%d%d", &n, &m)) {
		memset(head, 0, sizeof(head));
		memset(son, 0, sizeof(son));
		memset(f, 0, sizeof(f));
		memset(sz, 0, sizeof(sz));
		memset(top, 0, sizeof(top));
		memset(d, 0, sizeof(d));
		tot = 0;
		cnt = 0;
		for (int i = 1; i <= n; i++)
			scanf("%d", &b[i]);
		for (int i = 1; i <= n - 1; i++) {
			scanf("%d%d", &x, &y);
			add(x, y);
			add(y, x);
		}
		dfs1(1, 0);
		dfs2(1, 0, 1);
		build(1, 1, n);
		while(m--) {
			scanf("%d%d%d%d", &l, &r, &x, &y);
			cout << solve(l, r, x, y);
			if(m == 0) cout << endl;
			else cout << " ";
		}
	}
}

  

HDU 6162 樹鏈剖分