「HNOI2015」接水果 - 题解

「HNOI2015」接水果

题意:

给你一个树上路径集合$ S $ ,每条路径有个权值。每次询问一条路径 $ (x, y) $ ,问它在 $ S $ 中包含的路径中权值第 $ k $ 小的是多少。

做法:

首先考虑如何判断判断一条路径是否被另一条路径包含。
当一条路径 $ (x, y) (dep_x < dep_y) $ 是一条深度从浅到深的链时,路径 $ (u, v) (dep_u < dep_v) $ 包含路径 $ (x, y) $ 满足 $ v $ 在 $ y $ 的子树内、 $ u $ 在 $ x $ 的孩子、 $ y $ 的祖先 的子树外, $ u, v $ 在树的dfn序上为连续几段。
当一条路径 $ (x, y) (dep_x < dep_y) $ 不是一条深度从浅到深的链时,路径 $ (u, v) $ 包含路径 $ (x, y) $ 满足 $ v $ 在 $ y $ 的子树内、 $ u $ 在 $ x $ 的祖先 的子树外, $ u, v $ 在树的dfn序上为连续几段。
然后树套树维护路径 $ (x, y) $ 包含多少路径,树套树维护即可。

对于每一个询问,我们都可以二分答案。将集合 $ S $ 中的路径按权值排序,然后查询其中包含的排名即可。我们发现这可以整体二分。时间复杂度 $ O(n \log^3{n}) $

在Luogu上开O2快如风,在LOJ上被1s时限卡自闭。。。

其实树套树可以用扫描线代替的,时间复杂度 $ O(n \log^2{n}) $。

#include <bits/stdc++.h>
#define pb push_back
#define LNK(i, a) for(int i = hed[a]; i; i = nxt[i])
using namespace std;
const int N = 40010;
int n, m, q;
int cnt = 0, hed[N], to[N + N], nxt[N + N];
int dfn[N], low[N], idx = 0, fa[16][N], dep[N];
struct Plate {
	int x, y, w;
	inline bool operator<(const Plate &yy)const { return w < yy.w; }
}; Plate a[N];
struct Fruit {
	int x, y, k, id;
	inline bool operator<(const Fruit &yy)const { return x < yy.x; }
};
int pp[N], lp = 0, ans[N];

namespace Tree {
	struct Line {
		int x, y, w;
		Line(int X = 0, int Y = 0, int W = 0) : x(X), y(Y), w(W) {}
		inline bool operator<(const Line &yy)const { return x < yy.x; }
	}; Line t[N * 20]; int tot = 0;
	int tr[N];
	
	inline void add(int x, int w) { for(; x <= n; x += x & -x) tr[x] += w; }
	inline int ask(int x) {
		int ss = 0; for(; x; x -= x & -x) ss += tr[x]; return ss;
	}
	inline void mdy(int x, int y, int xx, int yy, int w) {
		t[++tot] = Line(x, xx, w), t[++tot] = Line(x, yy + 1, -w);
		t[++tot] = Line(y + 1, xx, -w), t[++tot] = Line(y + 1, yy + 1, w);
	}
}

void gi(int &x) {
	x = 0; register char c = getchar();
	for(; c < '0' || c > '9'; c = getchar());
	for(; c >= '0' && c <= '9';)
		x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
}
inline void add(int x, int y) {
	to[++cnt] = y, nxt[cnt] = hed[x], hed[x] = cnt;
}
void dfs(int u, int ff) {
	fa[0][u] = ff, dep[u] = dep[ff] + 1, dfn[u] = ++idx;
	for(int i = 1; i < 16; ++i) fa[i][u] = fa[i - 1][fa[i - 1][u]];
	LNK(i, u) if(to[i] ^ ff) dfs(to[i], u); low[u] = idx;
}
int LCA(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 15; ~i; --i) if(dep[fa[i][x]] >= dep[y]) x = fa[i][x];
	return x == y;
}
int find(int x, int y) {
	for(int i = 15; ~i; --i) if(dep[fa[i][y]] > dep[x]) y = fa[i][y];
	return y;
}
void solve(vector<Fruit> Q, int l, int r, int L, int R) {
	if(l >= r) {
		for(int i = 0; i < Q.size(); ++i) ans[Q[i].id] = pp[l]; return ;
	}
	int mid = (l + r) >> 1, pos; vector<Fruit> ls, rs;
	for(int i = L, lca, x, y; i <= R; ++i) {
		if(a[i].w > pp[mid]) break;
		pos = i, x = a[i].x, y = a[i].y, lca = LCA(x, y);
		if(lca) {
			x = find(x, y);
			Tree::mdy(dfn[y], low[y], 1, dfn[x] - 1, 1);
			Tree::mdy(dfn[y], low[y], low[x] + 1, n, 1);
			Tree::mdy(1, dfn[x] - 1, dfn[y], low[y], 1);
			Tree::mdy(low[x] + 1, n, dfn[y], low[y], 1);
		}
		else {
			Tree::mdy(dfn[x], low[x], dfn[y], low[y], 1);
			Tree::mdy(dfn[y], low[y], dfn[x], low[x], 1);
		}
	}
	sort(Tree::t + 1, Tree::t + Tree::tot + 1); int j = 1;
	for(int i = 0, K; i < Q.size(); ++i) {
		for(; j <= Tree::tot && Tree::t[j].x <= Q[i].x; ++j)
			Tree::add(Tree::t[j].y, Tree::t[j].w);
		K = Tree::ask(Q[i].y);
		K >= Q[i].k ? ls.pb(Q[i]) : (Q[i].k -= K, rs.pb(Q[i]));
	}
	for(; j <= Tree::tot; ++j) Tree::add(Tree::t[j].y, Tree::t[j].w);
	Tree::tot = 0;
	solve(ls, l, mid, L, pos), solve(rs, mid + 1, r, pos + 1, R);
}
int main() {
	gi(n), gi(m), gi(q);
	for(int i = 1, x, y; i < n; ++i) gi(x), gi(y), add(x, y), add(y, x);
	dfs(1, 0);
	for(int i = 1; i <= m; ++i) {
		gi(a[i].x), gi(a[i].y), gi(a[i].w);
		if(dep[a[i].x] > dep[a[i].y]) swap(a[i].x, a[i].y);
	}
	sort(a + 1, a + m + 1), pp[++lp] = a[1].w;
	for(int i = 2; i <= m; ++i) if(a[i].w ^ a[i - 1].w) pp[++lp] = a[i].w;
	vector<Fruit> Q;
	for(int i = 1, x, y, k; i <= q; ++i)
		gi(x), gi(y), gi(k), Q.pb((Fruit){dfn[x], dfn[y], k, i});
	sort(Q.begin(), Q.end());
	solve(Q, 1, lp, 1, m); for(int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2019-03-30 11:25  daniel14311531  阅读(227)  评论(1编辑  收藏  举报