Count On A Tree 1

题意

给定一颗带点权有根树,每次查询两点之间路径上点权的第\(k\)大值


解法

求第\(k\)大?我们立刻想到主席树

但是这是树上的问题,我们怎么进行操作呢?

我们令每一颗主席树维护当前节点到根节点路径上的点权,那么每个点的主席树都由其父亲转移过来

那么怎么查询\(x,y\)两个结点路径上的第\(k\)大点权呢?

我们发现,想要获得\(x,y\)两个结点之间的路径对应的主席树,我们不能简单的提取\(x,y\)这两个点对应的主席树进行计算,因为我们会发现在之前的定义下,有一些多余的结点也算了进来:就是它们的\(lca\)到根节点路径上的点

那么我们在线段树上二分时现在需要传入三个点所对应的主席树\(x,y,LCA(x,y)\),每次减去\(LCA(x,y)\)上对应的结点

但是这样我们会发现\(LCA\)也是\(x,y\)路径上的结点,而它被多减了一次

所以我们还需要传入\(LCA\)的父亲参与线段树上二分

把整个过程可以理解为一个前缀和的操作(本身主席树就利用的是前缀和的思想)

最后成功的将\(x,y\)的路径提取出来


代码

#include <iostream>
#include <algorithm>

using namespace std;

void fast_IO();

const int N = 2e5 + 10;

int n, m, t, lstans;

int a[N], rt[N];

int w[N], dep[N], f[N][25];

int cap;
int head[N], to[N], nxt[N];

struct CTree {
	
	int sz;
	int ls[N * 20], rs[N * 20], val[N * 20];
	
	void clear() { sz = 0; }
	
	int newnode() {
		++sz;
		ls[sz] = rs[sz] = val[sz] = 0;
		return sz;	
	}
	
	void mkchain(int &x, int y, int l, int r, int k) {
		x = newnode();
		ls[x] = ls[y], rs[x] = rs[y], val[x] = val[y] + 1;
		if (l == r)	return;
		int mid = l + r >> 1;
		if (k <= mid)
			mkchain(ls[x], ls[y], l, mid, k);
		else
			mkchain(rs[x], rs[y], mid + 1, r, k);
	}
	
	int query(int x, int y, int z, int fz, int l, int r, int k) {
		if (l == r)	return l;
		int tot = val[ls[x]] - val[ls[z]] + val[ls[y]] - val[ls[fz]];
		int mid = l + r >> 1;
		if (k <= tot)
			return query(ls[x], ls[y], ls[z], ls[fz], l, mid, k);
		else	
			return query(rs[x], rs[y], rs[z], rs[fz], mid + 1, r, k - tot);
	}	
	
} tr;

inline void add(int x, int y) {
	to[++cap] = y, nxt[cap] = head[x], head[x] = cap;
}

void discre() {
	for (int i = 1; i <= n; ++i)	a[i] = w[i];
	sort(a + 1, a + n + 1);
	t = unique(a + 1, a + n + 1) - a - 1;
	for (int i = 1; i <= n; ++i)
		w[i] = lower_bound(a + 1, a + t + 1, w[i]) - a;
}

void DFS(int x, int fa) {
	tr.mkchain(rt[x], rt[fa], 1, t, w[x]);
	dep[x] = dep[fa] + 1, f[x][0] = fa;
	for (int i = 1; i <= 20; ++i)	f[x][i] = f[f[x][i - 1]][i - 1];
	for (int i = head[x]; i; i = nxt[i]) 
		if (to[i] != fa)	DFS(to[i], x);
}

int LCA(int x, int y) {
	if (dep[x] < dep[y])	swap(x, y);
	for (int i = 20; i >= 0; --i)
		if (dep[f[x][i]] >= dep[y])	x = f[x][i];
	if (x == y)	return y;
	for (int i = 20; i >= 0; --i)
		if (f[x][i] != f[y][i])	x = f[x][i], y = f[y][i];
	return f[x][0];
}

int main() {
	
	fast_IO();
	
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)	cin >> w[i];
	
	discre();
	
	int x, y;
	for (int i = 1; i < n; ++i) {
		cin >> x >> y;
		add(x, y), add(y, x);
	}
	
	tr.clear();
	DFS(1, 0);
	
	int k, lca, flca;
	for (int i = 1; i <= m; ++i) {
		cin >> x >> y >> k;
		x ^= lstans;
		lca = LCA(x, y), flca = f[lca][0];
		cout << (lstans = a[tr.query(rt[x], rt[y], rt[lca], rt[flca], 1, t, k)]) << endl;
	}
		
	return 0;
}

void fast_IO() {
	ios :: sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);
}
posted @ 2019-09-05 22:25  四季夏目天下第一  阅读(149)  评论(0编辑  收藏  举报