「SPOJ COT」 Count on a tree

Posted -wallace-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「SPOJ COT」 Count on a tree相关的知识,希望对你有一定的参考价值。

Description

给定一个包含 (n) 个结点的树. 树节点从 (1)(n) 编号.。每个节点有一个整数权值。

执行以下操作 (m) 次:

u v k : 询问从节点 (u) 到 节点 (v) 的路径(包括端点)上的第 (k) 小的权值。

Hint

(1le n, mle 10^5)

Solution

求 Kth ,显然主席树。

问题是怎么放到树上。

(T_x) 为从根节点(暂定为 1 ,下同)到结点 (x) 的路径(包括端点)中权值的“前缀主席树”,即 (T_x) 维护这条路径上的权值的集合。

那么我们用一下前缀和的思想,若要得到路径 (u ightarrow v) 的主席树,那么只要:

[T_{u ightarrow v} = T_u + T_v - T_{ ext{LCA}(u, v)} - T_{ ext{father}( ext{LCA}(u, v))} ]

就行了。但实际上不用先把整个 (T_{u ightarrow v}) 求出来,到某个结点的位置时临时计算即可。

LCA 什么的可以上树剖或倍增(下面的代码用了树剖)。

  • 时间复杂度 (O((n+m)log n))
  • 空间复杂度 (O(nlog n))

Code

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 1e5 + 5;
const int S = N << 5;

int lc[S], rc[S], sum[S], total = 0;
int root[N], wei[N];
int n, q;
vector<int> G[N];

#define mid ((l + r) >> 1)
int build(int l, int r) {
	int rt = ++ total;
	if (l == r) return rt;
	lc[rt] = build(l, mid);
	rc[rt] = build(mid + 1, r);
	return rt;
}
int update(int pre, int pos, int l, int r) {
	int rt = ++ total;
	lc[rt] = lc[pre], rc[rt] = rc[pre];
	sum[rt] = sum[pre] + 1;
	if (l == r) return rt;
	if (pos <= mid) lc[rt] = update(lc[pre], pos, l, mid);
	else rc[rt] = update(rc[pre], pos, mid + 1, r);
	return rt;
}
int findKth(int a, int b, int c, int d, int k, int l, int r) {
	if (l == r) return l;
	int x = sum[lc[a]] + sum[lc[b]] - sum[lc[c]] - sum[lc[d]];
	if (k <= x) return findKth(lc[a], lc[b], lc[c], lc[d], k, l, mid);
	else return findKth(rc[a], rc[b], rc[c], rc[d], k - x, mid + 1, r);
}
#undef mid

int fa[N], size[N];
int dep[N], maxs[N];
int top[N];

void Dfs1(int rt, int f) {
	root[rt] = update(root[f], wei[rt], 1, n);
	size[rt] = 1, fa[rt] = f, dep[rt] = dep[f] + 1;
	for (vector<int>::iterator it = G[rt].begin(); it != G[rt].end(); ++ it) {
		if (*it == f) continue;
		Dfs1(*it, rt), size[rt] += size[*it];
		if (size[maxs[rt]] < size[*it])
			maxs[rt] = *it;
	}
}
void Dfs2(int rt,int tp) {
	top[rt] = tp;
	if (maxs[rt]) Dfs2(maxs[rt], tp);
	for (vector<int>::iterator it = G[rt].begin(); it != G[rt].end(); ++ it)
		if (*it != fa[rt] && *it != maxs[rt])
			Dfs2(*it, *it);
}
inline int findLCA(int u,int v) {
	while(top[u] != top[v])
		if (dep[top[u]] > dep[top[v]]) u = fa[top[u]];
		else v = fa[top[v]];
	return dep[u] < dep[v] ? u : v;
}

int buf[N], cnt;
signed main() {
	ios::sync_with_stdio(false);
	cin >> n >> q;
	for (register int i = 1; i <= n; ++ i)
		cin >> wei[i], buf[i] = wei[i];
	for (register int u, v, i = 1; i < n; ++ i) {
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	
	sort(buf + 1, buf + 1 + n), cnt = unique(buf + 1, buf + 1 + n) - buf - 1;
	for (register int i = 1; i <= n; i ++)
		wei[i] = lower_bound(buf + 1, buf + 1 + cnt, wei[i]) - buf;
		
	root[0] = build(1, n);
	Dfs1(1, 0), Dfs2(1, 1);
	
	while (q --) {
		int u, v, k, l, f;
		cin >> u >> v >> k;
		l = findLCA(u, v), f = fa[l];
		cout << buf[findKth(root[u], root[v], root[f], root[l], k, 1, n)] << endl;
	}
	return 0;
}

以上是关于「SPOJ COT」 Count on a tree的主要内容,如果未能解决你的问题,请参考以下文章

SPOJ 10628. SPOJ COT Count on a tree

SPOJ - COT2 Count on a tree II

Count on a tree SPOJ - COT

「SPOJ COT」 Count on a tree

SPOJ:COT2 Count on a tree II

SPOJ - COT2 Count on a tree II