Table of Contents
0-1字典树
例题1. CSU 1216:异或最大值: 给定一些数,任意两个数的最大异或值
例题2. HDU 4825 Xor Sum:每次询问给出一个数,找出一个与它异或结果最大的数
例题3. HDU 5536 Chip Factory: 计算 (s[i] + s[j]) ^ s[k] 的最大值
例题4. POJ 3764 The xor-longest Path : 在树上找一段路径使得异或和最大
例题5. BZOJ 4260: Codechef REBXOR:求两个不相交的区间异或和的最大值
例题6. CodeForces Mail.Ru Cup 2018 Round 2 F Tree and XOR:找出树上所有路径异或和的第K大
0-1字典树主要用于解决求异或最值的问题,0-1字典树其实就是一个二叉树,和普通的字典树原理类似,只不过把插入字符改成了插入二进制串的每一位(0或1)。
下面先给出0-1字典树的简单模板:
LL val[32 * MaxN]; //点的值
int ch[32 * MaxN][2]; //边的值
int tot; //节点个数
void add(LL x) { //往 01字典树中插入 x
int u = 0;
for(int i = 32; i >= 0; i--) {
int v = (x >> i) & 1;
if(!ch[u][v]) { //如果节点未被访问过
ch[tot][0] = ch[tot][1] = 0; //将当前节点的边值初始化
val[tot] = 0; //节点值为0,表示到此不是一个数
ch[u][v] = tot++; //边指向的节点编号
}
u = ch[u][v]; //下一节点
}
val[u] = x; //节点值为 x,即到此是一个数
}
LL query(LL x) {
int u = 0;
for(int i = 32; i >= 0; i--) {
int v = (x >> i) & 1;
//利用贪心策略,优先寻找和当前位不同的数
if(ch[u][v^1]) u = ch[u][v^1];
else u = ch[u][v];
}
return val[u]; //返回结果
}
不难发现以下事实:
复杂度:O(32*n)
http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1216
Description
给定一些数,求这些数中两个数的异或值最大的那个值。
(对于一个长度为 n 的数组a1, a2, …, an,请找出不同的 i, j,使 ai ^ aj 的值最大)
Input
多组数据。第一行为数字个数n,1 <= n <= 10 ^ 5。接下来n行每行一个32位有符号非负整数。
Output
任意两数最大异或值
贪心找最大异或值:
把每一个数以二进制形式从高位到低位插入trie树中,依次枚举每个数,在trie中贪心,即当前为0则向1走,为1则向0走。
异或运算有一个性质,就是对应位不一样则为1,要使结果最大化,就要让越高的位为1,所以找与一个数使得两数的异或结果最大,就需要从树的根结点(也就是最高位)开始找,如果对应位置的这个数是0,优先去找那一位为1的数,否则再找0;同理,如果对应位置的这个数是1,优先去找那一位为0的数,否则再找1。最终找到的数就是跟这个数异或结果最大的数。
对于n个数,每个数找一个这样的数并算出结果求其中的最大值即可。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
http://acm.hdu.edu.cn/showproblem.php?pid=4825
Problem Description
Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大。Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助。你能证明人类的智慧么?
Input
输入包含若干组测试数据,每组测试数据包含若干行。
输入的第一行是一个整数T(T < 10),表示共有T组数据。
每组数据的第一行输入两个正整数N,M(<1=N,M<=100000),接下来一行,包含N个正整数,代表 Zeus 的获得的集合,之后M行,每行一个正整数S,代表 Prometheus 询问的正整数。所有正整数均不超过2^32。
Output
对于每组数据,首先需要输出单独一行”Case #?:”,其中问号处应填入当前的数据组数,组数从1开始计算。
对于每个询问,输出一个正整数K,使得K与S异或值最大。
m组询问,每次询问给出一个数,求在n个数中找出一个数,使得与当前数的异或结果最大。
与上一题基本一样
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
http://acm.hdu.edu.cn/showproblem.php?pid=5536
Problem Description
John is a manager of a CPU chip factory, the factory produces lots of chips everyday. To manage large amounts of products, every processor has a serial number. More specifically, the factory produces n chips today, the i-th chip produced this day has a serial number si.
At the end of the day, he packages all the chips produced this day, and send it to wholesalers. More specially, he writes a checksum number on the package, this checksum is defined as below:
maxi,j,k(si+sj)⊕sk
which i,j,k are three different integers between 1 and n. And ⊕ is symbol of bitwise XOR.
Can you help John calculate the checksum number of today?
Input
The first line of input contains an integer T indicating the total number of test cases.
The first line of each test case is an integer n, indicating the number of chips produced today. The next line has n integers s1,s2,..,sn, separated with single space, indicating serial number of each chip.
1≤T≤1000
3≤n≤1000
0≤si≤109
There are at most 10 testcases with n>100
Output
For each test case, please output an integer indicating the checksum number in a line.
在一个数组中找出 (s[i] + s[j]) ^ s[k] 的最大值,其中 i、j、k 各不相同。
由于题目中的数据范围很小,可以暴力枚举 i 和 j,与上面的例题不同的是,由于规定 i, j, k 各不相同,所以需要增加一个 update 操作,用来记录增加或减少一个数后每个节点的访问次数,通过访问次数是否大于0判断当前数是否被使用过(也就是a[i], a[j])。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
http://poj.org/problem?id=3764
Description
In an edge-weighted tree, the xor-length of a path p is defined as the xor sum of the weights of edges on p:
⊕ is the xor operator.
We say a path the xor-longest path if it has the largest xor-length. Given an edge-weighted tree with n nodes, can you find the xor-longest path?
Input
The input contains several test cases. The first line of each test case contains an integer n(1<=n<=100000), The following n-1 lines each contains three integers u(0 <= u < n),v(0 <= v < n),w(0 <= w < 2^31), which means there is an edge between node u and v of length w.
Output
For each test case output the xor-length of the xor-longest path.
在树上找一段路径(连续)使得边权相异或的结果最大。
首先考虑异或的一个性质:0 ^ a = a,a ^ a = 0。前 i 个数的异或结果和前 j 个数的异结果再进行异或: pre[i] ^ pre[j] = a[i+1] ^ a[i+2] ^ …^ a[j] (i < j)。
假设dp[i] 表示前 i 个数中任意区间异或后的最大值,那么dp[i] = max(dp[i-1], query(pre[i])),然后把 pre[i] 插入到 01字典树中,可以依次求与 pre[i] 异或和的最大值。
那么dp[n]就是要求的答案,也就是ans = max(ans, query(pre[i])).
使用vector会超时。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
https://www.lydsy.com/JudgeOnline/problem.php?id=4260
给出 n 个数,求两个不相交的区间中的元素异或后的和的最大值
首先考虑前缀异或和以及后缀异或和。
于是可以通过先求出异或的前缀 pre[i] 和后缀 suf[i]。dp[i] 表示前 i 个数中任意区间异或后的最大值,那么dp[i] = max(dp[i-1], query(pre[i])),然后把 pre[i] 插入到 01字典树中,可以依次求与 pre[i] 异或和的最大值,后缀异或和同理。
这样对于每个 pre[i] 就会和之前的 i-1 个异或前缀和的共有部分相抵消,也就相当于是求任意区间的异或结果的最大值了。这样求出了一个区间,同理可利用后缀和求出另一个区间。
那么答案就是ans = max(ans, query(suf[i]) + dp[i-1])。
那么如何保证两个区间不相交呢?可以通过使前后两个区间一个为不包含第 i 个数的前部分区间,一个是包含第 i 个数的后部分区间就可以了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
http://codeforces.com/problemset/problem/1055/F
You are given a connected undirected graph without cycles (that is, a tree) of nn vertices, moreover, there is a non-negative integer written on every edge.
Consider all pairs of vertices (v,u)(v,u) (that is, there are exactly n2n2 such pairs) and for each pair calculate the bitwise exclusive or (xor) of all integers on edges of the simple path between vv and uu. If the path consists of one vertex only, then xor of all integers on edges of this path is equal to 00.
Suppose we sorted the resulting n2n2 values in non-decreasing order. You need to find the kk-th of them.
找出树上所有路径异或和的第K大