洛谷P8201 生活在树上(Hard Version) 题解
作者:互联网
题目链接: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\) 的各种情况,可得:
- \(lca(x,y)=x\)
- \(lca(x,y)=y\)
- \(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