【字典树+XOR】 LibreOJ 10050 The XOR Largest Pair

本文介绍了一种结合字典树存储和贪心算法的方法,用于从高位选择相反数,实现高效的数值处理。通过C++代码示例,详细展示了如何构建字典树并进行查询和添加操作,以最大化目标值。


字典树存储+贪心每次从高位选相反的


//#include <iostream>
//#include <cstring>
//#include <iomanip>
//#include <algorithm>
//#include <queue>
//#include <stack>
//#include <vector>
//#include <set>
//#include <map>
//#include <cmath>
//#include <cstdio>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef __int128 bll;
const ll maxn = 1e7+100;
const ll mod = 1e9+7;
const ld pi = acos(-1.0);
const ll inf = 1e18;
const ld eps = 1e-5;
const ld e = exp(1);

ll n,trie[maxn][2],ans = -inf,cnt = 1;

void add(ll str)
{
	ll to = 1;
		
	for(ll i = 32; i >= 0; i--)
	{
		ll k = (str >> i) & 1; //str在二进制下的第i位
		
		if(trie[to][k] == 0)
		{
			cnt++;
			trie[to][k] = cnt;
		}
		
		to = trie[to][k];
	}
	return ;
}

ll query(ll str)
{
	ll res = 0,to = 1;
	
	for(ll i = 32; i >= 0; i--)
	{
		ll k = (str >> i) & 1;
		
		if(trie[to][k^1] != 0)
		{
			to = trie[to][k^1];
			res += (1 << i);
		}
		else
		{
			to = trie[to][k];
		}
	
	}
	
	return res;	
}

int main()
{	
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
		
	cin >> n;
	
	for(ll i = 1; i <= n; i++)
	{
		ll x;
		cin >> x;
		
		ans = max(ans,query(x));
		add(x);
	}
	
	cout << ans << endl;
	
	return 0;
}

 

<think>我们已知:1.定义dx[i]为根节点(假设为0)到节点i的路径上所有边权的异或值。通过一次DFS可以求出所有dx[i]。2.树上任意两点u和v之间的路径异或值等于dx[u]xordx[v](因为从根到u和根到v的路径中,重叠的部分会相互抵消,剩下u到v的路径)。3.问题转化为:在dx数组(有n个数)中,找到两个数,使它们的异或值最大。使用01-Trie树来解决最大异或对问题:-将每个数的二进制表示(从高位到低位)插入到Trie树中(固定位数,比如31位,因为w<2^31,所以最多31位)。-然后对于每个数,在Trie树中查询与它异或结果最大的数:从根开始,每次尽量走与当前位相反的位(这样异或后当前位为1),如果没有相反的位则走相同的位。步骤:1.建树:使用邻接表存储树结构。2.DFS遍历树,计算每个节点的dx值(从根节点0开始,根节点的dx值为0)。3.构建01-Trie:将所有dx值插入Trie树。4.对每个dx值,在Trie树中查询最大异或值,然后取所有结果的最大值。注意:由于树是无根的,我们任意选择一个根(比如0号节点)即可。代码实现细节:-Trie树节点:每个节点有两个孩子,分别表示0和1。-插入:从高位到低位,根据每一位的值(0或1)建立路径。-查询:从高位到低位,尽量选择与当前位不同的分支(如果存在),这样异或后该位为1。时间复杂度:O(n*31)≈O(n),可以接受。注意:多组测试数据,需要初始化。下面给出C++代码实现:</think>### 带权树上最长异或路径的C++实现(使用Trie优化) #### 算法核心思想 1. **路径异或转换** 定义$d[u]$为节点$u$到根节点的路径异或和,则任意两点$u,v$间路径的异或和为: $$ \text{XOR}(u,v) = d[u] \oplus d[v] $$ 这个性质成立是因为路径重叠部分会相互抵消[^2]。 2. **问题转化** 原问题等价于: $$ \max_{u,v \in V} \left( d[u] \oplus d[v] \right) $$ 3. **01-Trie优化** 使用二进制字典树高效查询最大异或对: - 将每个$d[i]$的二进制表示插入Trie - 对每个$d[i]$,在Trie中查询能产生最大异或值的路径 #### C++代码实现 ```cpp #include <iostream> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 100010; const int BIT_LEN = 31; // 31位整数处理 struct Edge { int v, w; Edge(int _v, int _w) : v(_v), w(_w) {} }; vector<Edge> graph[MAXN]; int d[MAXN]; // 存储节点到根节点的异或和 // 01字典树节点 struct TrieNode { TrieNode* child[2]; TrieNode() { child[0] = child[1] = nullptr; } }; // DFS计算前缀异或和 void dfs(int u, int parent, int cur_xor) { d[u] = cur_xor; for (const Edge& e : graph[u]) { if (e.v == parent) continue; dfs(e.v, u, cur_xor ^ e.w); } } // 插入数字到字典树 void insertTrie(TrieNode* root, int num) { TrieNode* node = root; for (int i = BIT_LEN; i >= 0; i--) { int bit = (num >> i) & 1; if (!node->child[bit]) { node->child[bit] = new TrieNode(); } node = node->child[bit]; } } // 查询与num异或的最大值 int queryTrie(TrieNode* root, int num) { TrieNode* node = root; int max_xor = 0; for (int i = BIT_LEN; i >= 0; i--) { int bit = (num >> i) & 1; int toggled = 1 - bit; // 期望的相反位 if (node->child[toggled]) { max_xor |= (1 << i); node = node->child[toggled]; } else { node = node->child[bit]; } } return max_xor; } int main() { int n; while (cin >> n) { // 初始化 for (int i = 0; i < n; i++) graph[i].clear(); memset(d, 0, sizeof(d)); // 读入树结构 for (int i = 0; i < n-1; i++) { int u, v, w; cin >> u >> v >> w; graph[u].push_back(Edge(v, w)); graph[v].push_back(Edge(u, w)); } // DFS计算前缀异或和 dfs(0, -1, 0); // 构建字典树 TrieNode* root = new TrieNode(); for (int i = 0; i < n; i++) { insertTrie(root, d[i]); } // 查询最大异或值 int max_xor = 0; for (int i = 0; i < n; i++) { max_xor = max(max_xor, queryTrie(root, d[i])); } cout << max_xor << endl; // 释放字典树内存(实际竞赛可省略) // ... 遍历删除Trie节点 ... } return 0; } ``` #### 关键组件解析 1. **数据结构** - `Edge`:存储邻接点和边权 - `TrieNode`:字典树节点,包含0/1两个分支 - `d[]`:存储节点到根节点的异或和 2. **核心函数** - `dfs()`:深度优先遍历计算路径异或和 - `insertTrie()`:将数字插入01字典树 - `queryTrie()`:在字典树中查询最大异或值 3. **算法流程** ```mermaid graph TD A[输入树结构] --> B[DFS计算d数组] B --> C[构建01字典树] C --> D[遍历d数组查询最大异或] D --> E[输出结果] ``` #### 复杂度分析 - **时间复杂度**:$O(n \log W)$ DFS遍历$O(n)$,字典树操作$O(\log W)$,其中$W$为最大边权 - **空间复杂度**:$O(n \log W)$ 字典树存储所有二进制表示 #### 测试用例 ```plaintext 输入: 4 0 1 3 1 2 5 1 3 6 计算过程: d[0]=0(0000), d[1]=3(0011) d[2]=3^5=6(0110), d[3]=3^6=5(0101) 最大异或: d[0]^d[2]=0^6=6(0110) → 最大值 输出:6 ``` > 该实现基于路径异或性质[^2]和Trie树优化[^1],可高效处理$n \leq 10^5$的数据规模。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值