首页 > 其他分享 >【树上启发式合并】CF1709E XOR Tree

【树上启发式合并】CF1709E XOR Tree

时间:2024-01-14 10:56:27浏览次数:19  
标签:XOR int 合并 路径 Tree fa CF1709E 启发式 dis

XOR Tree

\(\mathtt{TAGS}\):树上启发式合并 + 异或 + 贪心

\(\mathtt{ESTIMATION}\):非常好的启发式合并题目

First.如何去除 \(0\) 路径

对于一条路径 \(u \to v\),要使其不为 \(0\) 肯定是将 \(\mathtt{LCA} (u,v)\) 变为 \(2 ^ {30 + x}\) 最好,这样异或值的第 \(30 + x\) 位一定为 \(1\)(因为 \(a_i \le 2 ^ {30}\)),修改之后,\(u,v\) 在 \(\mathtt{LCA} (u,v)\) 为根的子树内的路径都一定不为 \(0\) 了。显然,这样是最优的。

Second.如何快速判断一颗子树下有无 \(0\) 路径

首先,对于 \(u \to v\) 的路径权值 \(dis_{u, v}\) 可以化为 \(dis_{u, 1} \oplus dis_{v, 1} \oplus a_{\mathtt{lca}(u,v)}\)。

其实知道这个就可以去做 P4551 了。

这个时候可以用一个 set 储存该子树所有 \(dis_{u, 1}\) 的值。然后枚举一个 \(v\) 查找有无 \(dis_{v, 1} \oplus a_u\) 在其中,如果有,那么他们异或起来为 \(0\),就是有 \(0\) 路径。

然后就是把这个子树删除,set 合并到它的父亲上。

这样,大体思路就出来了,暴力的话时间复杂度 \(\text{O}(n ^ 2 \log n)\)(枚举节点的 \(n ^ 2\) 和 set 的 \(\log n\))。

Third.优化时间复杂度

启发式合并,每次将大小小一些的集合合并到大一些的集合上,执行 \(\log n\) 次,节点数就会达到 \(n\)。所以只会合并 \(\log n\) 次。

总时间复杂度为 \(\text{O}(n \log^2 n)\)。

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;
}

标签:XOR,int,合并,路径,Tree,fa,CF1709E,启发式,dis
From: https://www.cnblogs.com/Ice-lift/p/17963440

相关文章

  • uniDBtree树形显示
    跟ExpressDBTreeView学习(06)原理一样,参考系统自带Demo:示例代码下载:C:\ProgramFiles(x86)\FMSoft\Framework\uniGUI\Demos\Desktop\Grid-DBTreeGrid只要设置数据库里的ID,和其对应的ParentId,即可,UniGui会自动生成树显示: ......
  • Go+Gin+xorm+MySql实现增删改查
    一、概述承接上一篇(ps:原生增删改查),本篇使用xorm实现增删改查。之所以要使用xrom是因为xrom可以极大的缩小操作数据库的成本。使用rom之前需要导入响应的包gogetgithub.com/go-xorm/xorm#安装xormgogetxorm.io/coregoget-ugithub.com/go-sql-driver/mys......
  • CF1527D MEX Tree 题解
    思路如果一条路径的\(\text{mex}=k\),那么\(0\simk-1\)这些点一定在路径中出现过,并且一定在一条链上。如果不在一条链上,那么就不满足简单路径这一条件了。因此我们在从小到大加点的过程中如果发现一个点不在已求出的链上,那么比这个点编号大的\(k\)答案一定都是\(0\)了......
  • SourceTree SSH第一次登录需要交互确认的问题
    问题在SourceTreeSSH配置完ssh之后向上提交代码的时候发现:Theserver'shostkeyisnotcachedintheregistry.Youhavenoguaranteethattheserveristhecomputeryouthinkitis.Theserver'srsa2keyfingerprintis:ssh-rsa2048**:**:**:**:**:**:**:**:**:*......
  • SourceTree SSH第一次登录需要交互确认的问题
    问题在SourceTreeSSH配置完ssh之后向上提交代码的时候发现:Theserver'shostkeyisnotcachedintheregistry.Youhavenoguaranteethattheserveristhecomputeryouthinkitis.Theserver'srsa2keyfingerprintis:ssh-rsa2048**:**:**:**:**:**:**:**:**:......
  • CF1254D Tree Queries
    TreeQueriesLuoguCF1254D题面翻译给定一棵\(N\)个节点的树,有\(Q\)次操作。\(1\v\d\)给定一个点\(v\)和一个权值\(d\),等概率地选择一个点\(r\),对每一个点\(u\),若\(v\)在\(u\)到\(r\)的路径上,则\(u\)的权值加上\(d\)(权值一开始为\(0\))。\(2\v\)查......
  • CF1320E Treeland and Viruses
    TreelandandVirusesLuoguCF1320E题面翻译有一棵有\(n\)个节点的树,\(q\)次询问(询问互相独立),每次给定\(k_i\)个颜色,每个颜色有一个起始点\(v_j\)和移动速度\(s_j\),每一个颜色在每一次操作中会使它周围没有被染色的连通块上与它的距离不超过\(s_j\)的点全部染为这一......
  • B - Christmas Trees
    B-ChristmasTreeshttps://atcoder.jp/contests/abc334/tasks/abc334_b 思路对于起始种树点A在[L,R]区间的位置情况,三种A<LA>RA>=L,A<=R Codehttps://atcoder.jp/contests/abc334/submissions/48822474LLa,m,l,r;intmain(){cin>>a>&g......
  • CF1917F Construct Tree 题解
    Description给你一个数组\(l_1,l_2,\dots.l_n\)和一个数字\(d\)。问你是否能够构造一棵树满足以下条件:这棵树有\(n+1\)个点。第\(i\)条边的长度是\(l_i\)。树的直径是\(d\)。只需要判断是否有解即可。\(2\len\le2000,1\led\le2000,1\lel_i\led\)。Solutio......
  • CF1917F Construct Tree 题解
    题目链接:https://codeforces.com/contest/1917/problem/F题意有\(n\)条长度\(l_i\)的边,问它们是否能组成一棵\(n+1\)个节点的树,使得树的直径长度为\(d\)。\(n,d\le2000\)。题解首先当然要存在一个边集\(D\),使得\(\sum\limits_{i\inD}l_i=d\),这可以使用背包......