首页 > 其他分享 >CF1709E XOR Tree

CF1709E XOR Tree

时间:2024-04-25 13:36:04浏览次数:17  
标签:set XOR int Tree xor son CF1709E rm 节点

link

Solution:

PART 1: 转化

首先套路地预处理出每个节点到根节点(\(1\) 号节点)路径上的点权异或和 \(w[u]\) 。

可以发现题意容易转化为:给定一棵 \(n\) 个节点的树,问你最少可以把它分成多少个联通块,使得每个连通块中的节点两两路径上的异或和不为 0

易知对于一个节点,若它要被割分出原来的树,那么一定是在它的子树中有两个点对 \((x,y)\) 没有割分出原树且 \(w[x]\) \(\rm xor\) \(w[y]\) \(\rm xor\) \(a[\rm LCA(x,y)] = 0\)。

PRAT 2:暴力

考虑朴素做法:对每个节点开一个 \(\rm set\) 记录每个节点子树中没有被割分出去的所有节点的 \(w\) 值。枚举 \(\rm LCA(x,y)\)(令它为 \(u\)),如果对于节点 \(u\) 的儿子节点 \(v\),可以在 \(u\) 的 \(\rm set\) 中找到一个节点 \(t\),使得 \(w[t]\) \(\rm xor\) \(w[v]\) \(\rm xor\) \(a[u] = 0\) 。那么就一定要将点 \(u\) 割分出去。

具体来讲,我们暴力的合并节点 \(u\) 的所有儿子的 \(\rm set\),并且枚举其中的点 \(t\) ,若可以在 \(v\) 的 \(\rm set\) 中找到一个节点 \(t\) ,使得 \(w[t]\) \(\rm xor\) \(w[v]\) \(\rm xor\) \(a[u] = 0\),那么清空 \(u\) 的 \(\rm set\),并使答案加一

此时的最坏时间复杂度会在树是一个链的情况下退化成 \(O(n^2 \log n)\)。

PART 3:优化

钦定 \(son[u]\) 为 \(u\) 的所有子节点中子树大小最大的一个,称为重儿子,其他称为轻儿子。可以发现若朴素的合并所有轻儿子,并以 \(O(1)\) 的时间复杂度合并重儿子,那么每个节点至多被朴素合并 \(O(\log n)\) 次。

因此我们对于重儿子直接使用 \(O(1)\) 的 \(\rm swap\) 交换 \(u\) 与 \(son[u]\) 的 \(\rm set\) ,并只查询 \(w[u]\) \(xor\) \(a[u]\) 是否在 \(\rm set\) 中出现,此时相当于合并了。对于轻儿子,直接进行暴力的合并。

这种技巧被我们称作 \(\rm dsu\) \(\rm on\) \(\rm tree\) (树上启发式合并)

时间复杂度降为 \(O(n \log ^2 n)\)

code:

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

const int N = 2e5 + 10;
vector<int>G[N];
set<int>S[N];
int n, a[N], s[N], siz[N], son[N], ans;

void dfs(int u, int fa){
	siz[u] = 1;
	s[u] = a[u] ^ s[fa];
	for(auto v:G[u]){
		if(v == fa)continue;
		dfs(v, u);
		if(siz[v] > siz[son[u]]) son[u] = v;
		siz[u] += siz[v];
	}
	return;
}

void dsu(int u, int fa){
	bool res = 0;
	if(son[u]){
		dsu(son[u], u);
		if(S[son[u]].find(s[u] ^ a[u]) != S[son[u]].end()) res = 1;
		swap(S[u], S[son[u]]);
	}
	S[u].insert(s[u]);
	for(auto v:G[u]){
		if(v != fa && v != son[u]){
			dsu(v, u);
			for(auto t:S[v]) if(S[u].find(t ^ a[u]) != S[u].end()) res = 1;
			for(auto t:S[v]) S[u].insert(t);
		}
	}
	if(res){
		//cout << u << " " << res << "\n";	
		ans++;
		S[u].clear();
	}
	return;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++)cin >> a[i];
	for(int i = 1; i < n; i++){
		int x, y;
		cin >> x >> y;
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs(1, 0);
	dsu(1, 0);
	cout << ans;
	
	return 0;
}

标签:set,XOR,int,Tree,xor,son,CF1709E,rm,节点
From: https://www.cnblogs.com/little-corn/p/18157504

相关文章

  • CF771C Bear and Tree Jumps
    题目大意:给定一棵有\(n\)个节点的树,要你统计\(\sum_{1\lex\ley\len}{dist(x,y)/k}\)(\(dist(x,y)\)表示\(x\)到\(y\)的距离)\(n\le2\times10^5,k\le5\)解法:一道换根\(dp\)套路题。首先看到树上统计问题,考虑树形\(dp\),那么我们设\(g(u)\)为以\(......
  • CF911F Tree Destruction
    题目链接:https://www.luogu.com.cn/problem/CF911Fsolution:先求得树的直径,再求得在树的直径上的节点和不在树的直径上的节点。我们考虑优先删除不在直径上的节点,这样不会破坏树的直径,在删完了这些点之后再慢慢删直径上的点。#include<bits/stdc++.h>usingnamespacestd;#def......
  • AGC014D Black and White Trees
    传送门[AGC014D]BlackandWhiteTree给出一颗\(N\)个节点组成的树,每个节点都可以被染成白色或者黑色;有高桥(先手)和青木(后手)两个人————高桥可以把任意一个点染成白色,青木则可以把任意一个点染成黑色,每个点只可染色一次。重复上述操作直到所有点都被染色后,只执行一次执行......
  • Codeforces 1863F Divide, XOR, and Conquer
    记\(s_{l,r}=\oplus_{i=l}^ra_i\)。考虑到这个相当于是\([l,r]\)内分裂区间,可以考虑区间\(\text{DP}\)。即记\(f_{l,r}\)为\([l,r]\)区间是否能被遍历到。转移考虑对于\([l,r]\),考虑在已知的条件下(\(len\ger-l+1\))\([l,r]\)是否合法。即到这个状态......
  • ICPC World Finals Luxor 游记
    流水账开场我先看了一下J,简单贪了一下,没有什么结果,就丢给pb了。然后pb这时候抛过来一个C,我上去写完发现过不去样例,有点小爆。过了一会儿pb发现样例输入藏东西了,我改了改就过样例了,然后交了一发wa,血压有点拉起来了。fsy这时候上去签了A,我当时看了一遍C没瞪出问题,然......
  • 都2024年了,你还不知道git worktree么?
    三年前python大佬吉多·范罗苏姆(为Python程序设计语言的最初设计者及主要架构师)才知道gitworktree,我现在才知道,我觉得没啥丢人的。应用场景如果你正在feature的分支中开发新功能,线上版本紧急错误又需要你基于master做修复。可能有如下几种办法解决:解法1将本......
  • (复习)树上启发式合并(dsu on tree)入门U41492树上数颜色
    主要思想是树的重轻儿子之分使得时间复杂度为o(nlogn),神奇欲深入了解的这里:https://oi-wiki.org/graph/dsu-on-tree/点击查看代码#include<bits/stdc++.h>usingnamespacestd;typedefstructedge//边结构体{intto,next;}EDGE;//边相关数组EDGEe[100001<<1];......
  • UniTreeMenu只展开一个Item,点击主菜单时,不打开最后一个item
    设置:procedureTMainForm.UniTreeMenu1Click(Sender:TObject);varNode:TUniTreeNode;Ts:TUniTabSheet;FrC:TUniFrameClass;Fr:TUniFrame;FClassName,ShowInfo:string;beginNode:=UniTreeMenu1.Selected;ifNode.Tag>1000thenbeginTs:=Node.Data;......
  • Codeforces 1824C LuoTianyi and XOR-Tree
    考虑到肯定如果能在这个节点让子树的值尽量相同肯定更好,这样子不会与上面的操作相冲突。于是有个\(\text{DP}\)的思路。记\(f_{u,i}\)为\(u\)子树内叶子节点的值都变为\(i\)的最小代价。这个有一个很好的性质,就是\(\maxf_{u,i}-\minf_{u,i}=1\)。这是因为考......
  • P6018 [Ynoi2010] Fusion tree 题解
    题目链接:Fusiontree大部分人貌似用的边权01Trie,实际这题用点权01Trie类似文艺平衡树去写更方便。考虑两种常见的区间维护:线段树。使用的是父节点信息是归并了左右区间的信息,适用于不需要考虑父节点的贡献的信息。文艺平衡树。每个点就是一个信息,归并左右子树,外加当......