CF1709E XOR Tree(set 启发式合并)

· · 题解

Analysis

a_u 表示 u 的点权,d_u 表示树上 1u 简单路径上所有点的点权异或和。

题目中 uv 的简单路径上的所有点点权异或和 =0 等价于 d_u\oplus d_v\oplus a_{\text{lca}(u,v)}=0

可以证明如果允许改变 a_u,那么一定可以使以 u 为根的子树中不存在异或和为 0 的简单路径(一种构造方法是令 a_u=2^{u+11^{4514}})。

考虑对每个点开一个 set,记录其子树中(包括自身)所有点的 d。特别的,若一个点被改变,则其 set 为空 ^{[1]}

在枚举一个点 rt 的所有儿子时,设现有集合为 S,该儿子的集合为 T,则枚举 T 中的所有元素。设当前枚举元素为 T_i,在 S 中查找是否存在 a_{rt}\oplus T_i,若存在,则子树中一定存在两个点 u,v 满足 d_u\oplus d_v\oplus a_{rt}=0。因此一定要改变 a_{rt}

复杂度 $O(n^2\log n)$,用 启发式合并 可以使复杂度降低到 $O(n\log^2n)$。 ## Code ```cpp #include<bits/stdc++.h> using namespace std; #define For(i,j,k) for(int i=(j);i<=(k);i++) #define Rof(i,j,k) for(int i=(j);i>=(k);i--) #define eb emplace_back #define IOS ios::sync_with_stdio(false),cin.tie(0) #define int long long int n,res,a[200010],dis[200010]; vector<int> e[200010]; set<int> s[200010]; void dfs(int u,int fa){ bool tmp=0; s[u].insert(dis[u]); for(int v:e[u]) if(v!=fa){ dis[v]=dis[u]^a[v],dfs(v,u); if(s[u].size()<s[v].size()) swap(s[u],s[v]); for(int i:s[v]) if(s[u].find(a[u]^i)!=s[u].end()) tmp=1; for(int i:s[v]) s[u].insert(i); } if(tmp) res++,s[u].clear(); } signed main(){IOS; cin>>n; For(i,1,n) cin>>a[i]; For(i,1,n-1){ int u,v; cin>>u>>v; e[u].eb(v),e[v].eb(u); } dis[1]=a[1]; dfs(1,0); cout<<res; } ```