首页 > 其他分享 >P6374 「StOI-1」树上询问

P6374 「StOI-1」树上询问

时间:2023-01-12 18:55:49浏览次数:41  
标签:fa int text P6374 dep StOI lca 树上 dis

P6374 「StOI-1」树上询问

Description

给定一颗 \(n\) 个节点的树,有 \(q\) 次询问。

每次询问给一个参数三元组 \((a, b, c)\) ,求有多少个 \(i\) 满足这棵树在以 \(i\) 为根的情况下 \(a\) 和 \(b\) 的 \(\text{LCA}\) 是 \(c\)。

其中,\(1 \leq n \leq 5 \times 10^{5},\ 1 \leq q \leq 2 \times 10^{5}\)

Solution

看到数据范围,可以想到大概是 \(O(q \log n)\) 的复杂度,而且要求 \(\text{LCA}\),大概是需要我们先 \(O(n)\) 处理一下这棵树,然后寻找一些 \(\text{LCA}\) 性质关系来解决这个问题。

其实这个题目给的三个样例挺好的,我们完全可以在手玩样例的时候隐隐约约感受到正解,只不过需要再加上一些细节处理。

image-20230108175658648

我们拿这一颗树为例子吧。

如果三元组是 \((15, 19, 16)\) 也就是说 \(z\) 在 \(x,\ y\) 的路径上面并且 \(z\) 不是 \(x, \ y\) 的最近公共祖先,那么我们把 \(16\) 给拎起来,容易发现,答案的个数就是除了 \(16\) 到 \(19\) 这个路径上的 \(16\) 的子树,其余的子树的大小之和。

如果三元组是 \((1, 8, 3)\) 也就是 \(z\) 不在 \(x, \ y\) 的路径之上,那么无论我们让哪个节点作为根节点都不会成立,所以答案就是 0

如果三元组是 \((9, 12, 1)\) ,也就是说 \(z\) 正好就是 \(x, \ y\) 的最近公共祖先,那么答案就是整棵树除了公共祖先分别到 \(x, \ y\) 的子树的其余子树的大小之和。

再处理一些细节问题,比如说 \((20, 20, 20)\) 和 \((1, 12, 1)\) 这样的三元组。

Code

#include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 5;
int n, q, cnt, head[N], dep[N], fa[N][20], siz[N], lg[N];

struct Edge {
	int to, nxt;
}e[N << 1];

inline int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (!isdigit(c)) {
		if (c == '-') f = -1;
		c = getchar();
	}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
	return x * f;
}

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

inline void dfs(int x, int f) {
	dep[x] = dep[f] + 1;
	siz[x] = 1;
	fa[x][0] = f;
	for (int i = 1; i <= lg[dep[x]]; ++i) fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for (int i = head[x]; i ; i = e[i].nxt) {
		int to = e[i].to;
		if (to == f) continue;
		dfs(to, x);
		siz[x] += siz[to];
	}
}

inline int lca(int x, int y) {
	if (dep[x] < dep[y]) swap(x, y);
	while (dep[x] != dep[y]) x = fa[x][lg[dep[x] - dep[y]]];
	if (x == y) return x;
	for (int i = lg[dep[x]]; i >= 0; --i) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

inline int find_son(int x, int y) {
	if (x == y) return 0;
	while (dep[x] != dep[y] + 1) x = fa[x][lg[dep[x] - dep[y] - 1]];
	return x;
}

int main() {
	n = read(), q = read();

	int x, y;
	for (int i = 1; i <= n - 1; ++i) {
		x = read(), y = read();
		add(x, y);
		add(y, x);
	}

	for (int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
	dfs(1, 0);

	int z;
	while (q--) {
		x = read(), y = read(), z = read();
		if (x == y && z == x) {
			printf("%d\n", n);
			continue;
		}
		int l = lca(x, y);
		if (z == l) {
			int fx = find_son(x, z);
			int fy = find_son(y, z); // 处理相同的特殊情况
			printf("%d\n", siz[1] - siz[fx] - siz[fy]);
		} else if ((lca(x, z) == z || lca(y, z) == z) && dep[l] <= dep[z]) {
			if (lca(z, y) == l) {
				int fx = find_son(x, z);
				printf("%d\n", siz[z] - siz[fx]);
			}
			else {
				int fy = find_son(y, z);
				printf("%d\n", siz[z] - siz[fy]);
			}
		} else printf("0\n");
	}

	system("pause");
	return 0;
}

Summary

其中判断树上三个点 \(x, y, z\) ,\(z\) 是否位于 \(x, y\) 两个点间的路径之上。我们可以通过 \((\text{lca}(x, z) == z \ |\ | \ \text{lca}(y, z) == z)\ \&\& \ dep[z] >= dep[\text{lca}(x,y)]\) 来判断。

或者也可以用 \(dis\) 数组代表根到每个点的距离,对于 \(x, y, z\) 中 \(z\) 是否位于 \(x, \ y\) 路径之上可以用 \(dis[x] + dis[y] - 2 * dis[lca(a,b)] == dis[a] + dis[c] - 2 * dis[lca(a, c)] + dis[b] + dis[c] - 2 * dis[lca(b, c)]\) 来判断。

标签:fa,int,text,P6374,dep,StOI,lca,树上,dis
From: https://www.cnblogs.com/Miraclys/p/17047668.html

相关文章

  • 树上路径问题
    P5588 小猪佩奇爬树:P5588小猪佩奇爬树-洛谷|计算机科学教育新生态(luogu.com.cn)v用来存储各个颜色的节点一.v[i].size()=0时,不再赘述二.v[i].size()=1时,此......
  • 树形 dp 与树上问题
    NFLS集训笔记20220802-树形dp进阶与树上问题综合\(\text{ByDaiRuiChen007}\)I.洛谷[P2585]-三色二叉树\(\text{Link}\)思路分析简单题,建出树后暴力枚举当前......
  • 树上启发式合并
    树上启发式合并\(\text{ByDaiRuiChen007}\)一、算法简介在解决树上问题时,我们经常遇到需要统计多个节点各自的子树信息的情况,对于一般暴力统计的\(\Theta(n^2)\)复杂......
  • Dev-c++下‘stoi‘ was not declared in this scope解决办法
    打开“工具”->“编译选项”选择“代码生成/优化”,设置成以下形式即可......
  • canvasToImage报错:_canvas2image2.default.convertToImage is not a function
    vue2+js项目,某个屏幕截图功能,报错:代码中用到的importcanvasToImagefrom'canvas2image';打印canvasToImage发现是{},那么canvasToImage.convertToImage自然会报错。......
  • P3128 [USACO15DEC]Max Flow P 树上点差分
    //题意:给出一棵树,现在有一操作:给出两点,将两点之间的路径都加1,最后统计整棵树中值最大的点是谁//思路:树上路径问题,树剖+线段树可以解决,但是因为只是简单的维护区间加减,用......
  • 树上差分
    title:树上差分date:2022-11-1510:30:06tags:算法本文章遵守知识共享协议CC-BY-NC-SA,转载时须在文章的任一位置附上原文链接和作者署名(rickyxrc)。推荐在我的个人......
  • 省选03. 树上问题
    P2664树上游戏首先,将贡献拆成每种颜色对每个点的贡献。考虑已经选择了一种颜色,将这些颜色的点和所对应边全部删去,就得到了很多连通块。假设其中一个连通块的大小为\(s......
  • 洛谷P2680 运输计划(LCA + 二分 + 树上边差分)
    洛谷P2680运输计划​ 现在有一棵树,每条树边上都有正权值。接下来,有m个询问,每次询问给出两个结点,这两个结点之间有一条路径。现在你可以任选一条树边,将其边权置为0,请输......
  • 树上启发式合并
    树上问题都是神仙又称\(DSUontree\)前置芝士会重链剖分的思想(就是只会口胡就行)适用范围:我也不知道理论就是对每个结点,暴力统计其子树内的信息只不过在每个结点......