CF1709E XOR Tree 题解
XOR Tree
:树上启发式合并 + 异或 + 贪心
:非常好的启发式合并题目
First.如何去除 路径
对于一条路径
Second.如何快速判断一颗子树下有无 路径
首先,对于
其实知道这个就可以去做 P4551 了。
这个时候可以用一个 set 储存该子树所有
然后就是把这个子树删除,set 合并到它的父亲上。
这样,大体思路就出来了,暴力的话时间复杂度
Third.优化时间复杂度
启发式合并,每次将大小小一些的集合合并到大一些的集合上,执行
总时间复杂度为
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n;
vector<int> G[N];
int a[N], dis[N];
void dfs(int u, int fa) {
dis[u] = a[u];
if(fa) dis[u] ^= dis[fa];
for (auto v : G[u]) {
if(v != fa) dfs(v, u);
}
}
set<int> s[N]; // 快速查找有无 0 路径
void move (int u, int v) { // 合并
for (auto x : s[u]) s[v].insert(x);
s[u].clear();
}
int ans = 0;
void dfs2(int u, int fa) {
bool mark = 0;
s[u].insert(dis[u]);
for (auto v : G[u]) {
if(v != fa) {
dfs2(v, u);
if(s[u].size() < s[v].size()) swap(s[u], s[v]); // 保证是小的合并至大的里面【启发式合并】
for (auto x : s[v]) mark |= s[u].count(x ^ a[u]); // 如果存在 dis[u] = x ^ a[u] 说明子树中存在异或路径为 0 的路径
move(v, u);
}
}
if(mark) ans ++, s[u].clear(); // 如果存在,那么该节点需要删除
}
signed main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v, G[u].push_back(v), G[v].push_back(u);
}
dfs(1, 0);
dfs2(1, 0);
cout << ans << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现