ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

洛谷P8201 生活在树上(Hard Version) 题解

2022-07-06 21:36:25  阅读:159  来源: 互联网

标签:int 题解 Hard vis Version lca oplus id dis


题目链接:P8201 生活在树上(hard version)

题意

给定一个点带权的树,点数为 \(n\),第 \(i\) 个点的权值为 \(w_i\)。

定义两点之间的路径为路径上所有点的点权的按位异或和,即 \(dis(a,b)\)。

现在有 \(m\) 次询问,每次询问给定三个数 \(x,y,k\),问是否存在 \(t\),使得 \(dis(t,x)\oplus dis(t,y)=k\)?

\(n,m\leq 5*10^5,0\leq k,w_i\leq 10^7\)

简化与分析

这题有一个简单版,就是把点权转化成了边权,只要重复的一去掉,就看 \(dis(x,y)\) 的值是否为 \(k\) 即可。

现在考虑一下点权,考虑到 \(t\) 的各种情况,可得:

  1. \(lca(x,y)=x\)
  2. \(lca(x,y)=y\)
  3. \(lca(x,y)=t(t\not=x,y)\)

不管咋样,划分完各种情况后,我们发现,当 \((x,y)\) 路径上存在一个点 \(t\),使得 \(w_t\oplus dis(x,y)=k\) 时,询问成立。

\(dis(x,y)\) 可以直接一次 dfs 之后推出 \(S\) 数组,随后就转化为了 \(dis(x,y)=S_x\oplus S_y\oplus w_{lca}\)。

那么,问题就转化为了:\((x,y)\) 上是否存在点 \(t\),满足 \(w_t=W=S_x\oplus S_y\oplus w_{lca}\oplus k\)。

树上差分

LCA求法

这题在题面里面表明了要卡常数,所以直接 Tarjan 就行了(反之是离线)。

点的计数查询

询问是离线的,所以不必在线求。我们假设 \(lca(x,y)=t\),\(t\) 的父节点为 \(t'\),那么 \((x,y)\) 上符合要求的节点数为 \((root,x)+(root,y)-(root,t)-(root,t')\)。

我们记某次查询为 \((x,W,f)\) 为查询根节点到点 \(x\) 上权值为 \(W\) 的个数,\(f\) 标记要不要取反,那么一共有 \(4m\) 次查询。而他们是可以离线之后统一存下来,随后一次 DFS 时全部处理好(说不上来,感觉像 HH的项链 啥的题目也用了这种技巧),伪代码如下:

//w < 1e7,所以异或后至多是两倍大小
int vis[2e7+10];
void dfs(int x) {
    vis[w[x]]++;
    for (Query q : query[x])
        q.ans = q.f * vis[q.W];
    for (int y : son[x])
        dfs(y);
    vis[w[x]]--;
}

复杂度分析

主要的复杂度在于求 LCA 部分,考虑到 Tarjan 求 LCA 的复杂度在于并查集,所以复杂度视情况而已,从 \(O(n\log n)\) 到 \(O(n\alpha(n))\) 都有可能。后者只需要离线之后统一 \(O(n)\) 进行一次深搜即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
int n, m, w[N];
// Graph
vector<int> G[N];
//Query
struct Query { int x, y, k, lca, f_lca, id; };
vector<Query> query;
namespace Tarjan_LCA {
	int fa[N];
	void init() { for (int i = 1; i <= n; ++i) fa[i] = i; }
	int find(int x) {
		if (x != fa[x]) fa[x] = find(fa[x]);
		return fa[x];
	}
	void merge(int x, int y) { fa[find(x)] = find(y); }
	//Tarjan
	vector<int> Q[N];
	int vis[N], father[N];
	void Tarjan(int x, int f) {
		vis[x] = 1, father[x] = f;
		for (int y : G[x])
			if (y != f) Tarjan(y, x), merge(y, x);
		for (int id : Q[x]) {
			Query &q = query[id];
			int y = x ^ q.x ^ q.y;
			if (vis[y])
				q.lca = find(y), q.f_lca = father[q.lca];
		}
	}
	void solve() {
		for (Query q : query)
			Q[q.x].push_back(q.id), Q[q.y].push_back(q.id);
		init();
		Tarjan(1, 0);
	}
}
//dfs1
int S[N];
void dfs1(int x, int f) {
	S[x] = S[f] ^ w[x];
	for (int y : G[x]) if (y != f) dfs1(y, x);
}
//dfs2
int vis_S[20000010];
struct Query2 { int id, W, f; };
vector<Query2> qq[N];
int ans[N];
void dfs2(int x, int f) {
	++vis_S[w[x]];
	for (Query2 q : qq[x])
		ans[q.id] += vis_S[q.W] * q.f;
		for (int y : G[x])
			if (y != f) dfs2(y, x);
	--vis_S[w[x]];
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &w[i]);
	for (int i = 1; i < n; ++i) {
		int x, y;
		scanf("%d%d", &x, &y);
		G[x].push_back(y), G[y].push_back(x);
	}
	for (int i = 0; i < m; ++i) {
		int x, y, k;
		scanf("%d%d%d", &x, &y, &k);
		query.push_back((Query){x, y, k, -1, -1, i});
	}
	Tarjan_LCA::solve();
	dfs1(1, 0);
	for (Query q : query) {
		int W = S[q.x] ^ S[q.y] ^ w[q.lca] ^ q.k;
		qq[q.x    ].push_back((Query2){q.id, W,  1});
		qq[q.y    ].push_back((Query2){q.id, W,  1});
		qq[q.lca  ].push_back((Query2){q.id, W, -1});
		qq[q.f_lca].push_back((Query2){q.id, W, -1});
	}
	dfs2(1, 0);
	for (int i = 0; i < m; ++i)
		puts(ans[i] > 0 ? "Yes" : "No");
	return 0;
}

标签:int,题解,Hard,vis,Version,lca,oplus,id,dis
来源: https://www.cnblogs.com/cyhforlight/p/16452536.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有