http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1295
给出一个长度为N的正整数数组A,再给出Q个查询,每个查询包括3个数,L, R, X (L <= R)。求A[L] 至 A[R] 这R - L + 1个数中,与X 进行异或运算(Xor),得到的最大值是多少?
Input
第1行:2个数N, Q中间用空格分隔,分别表示数组的长度及查询的数量(1 <= N <= 50000, 1 <= Q <= 50000)。
第2 - N+1行:每行1个数,对应数组A的元素(0 <= A[i] <= 10^9)。
第N+2 - N+Q+1行:每行3个数X, L, R,中间用空格分隔。(0 <= X <= 10^9,0 <= L <= R < N)
Output
输出共Q行,对应数组A的区间[L,R]中的数与X进行异或运算,所能得到的最大值。
查找跟x异或的最大值显然是用字典树,然而这个题每个询问都有一个区间,是查询这个区间内的数和x的异或的最大值,这就比较忧伤了,刚开始套了个莫队,TLE了,然后直接在插入的时候记录trie里面的边属于哪个位置的数字,查询的时候看这条边是不是在查询区间内。。。
#include
using namespace std;
const int N = 50000 + 10, M = 2, INF = 0x3f3f3f3f;
int tot;
int len = 31, block_sz;
int a[N], ans[N];
struct node
{
node *next[M];
vector<int>vec;
void init()
{
memset(next, 0, sizeof next);
vec.clear();
}
}trie[N*31], *root;
node * new_node()
{
trie[tot].init();
return trie + tot++;
}
void trie_init()
{
tot = 0;
root = new_node();
}
void trie_insert(int val, int id)
{
node *p = root;
for(int i = len-1; i >= 0; i--)
{
int j = 1 & (val >> i);
if(p->next[j] == NULL) p->next[j] = new_node();
p->next[j]->vec.push_back(id);
p = p->next[j];
}
}
bool check(node* &p, int l, int r)
{
return upper_bound(p->vec.begin(), p->vec.end(), r) - lower_bound(p->vec.begin(), p->vec.end(), l);
}
int trie_query(int val, int l, int r)
{
node *p = root;
int ans = 0;
for(int i = len-1; i >= 0; i--)
{
int j = ! (1 & (val >> i));
if(p->next[j] && check(p->next[j], l, r))
{
ans = ans * 2 + 1;
p = p->next[j];
}
else
{
ans = ans * 2 + 0;
p = p->next[!j];
}
}
return ans;
}
int main()
{
int n, m;
trie_init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
trie_insert(a[i], i);
}
int x, l, r;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &l, &r);
l++, r++;
int ans = trie_query(x, l, r);
printf("%d\n", ans);
}
return 0;
}
//2017.09.02
学习了一下oi爷的可持久化字典树,代码简短凝练(oi爷的优秀传统?),跟主席树看上去非常像,每次只更新一条链,其他的复用以前的内容,更新查询都是递归函数,都可以改写成非递归的
#include
using namespace std;
const int N = 50000 + 10;
int son[N*35][2], sum[N*35];
int root[N];
int tot;
int len = 31;
bool bs[35];//储存待处理数字的二进制表示
//空树的话,所有值都是0,可以直接看作一个点,所以就直接免去了建空树的过程,要建的话,参考主席树即可
void trie_insert(int p, int pre, int &x)
{
x = ++tot;
son[x][0] = son[pre][0], son[x][1] = son[pre][1];
//memcpy(son[x], son[pre], sizeof(int) * 2);
sum[x] = sum[pre] + 1;
if(! p) return;
trie_insert(p-1, son[pre][bs[p-1]], son[x][bs[p-1]]);
}
int trie_query(int p, int st, int en)
{//此时bs储存的是跟查询值完全相反的二进制位,即查询值要取得异或最大值所需要的二进制位
if(! p) return 0;
if(sum[son[en][bs[p-1]]] > sum[son[st][bs[p-1]]]) return trie_query(p-1, son[st][bs[p-1]], son[en][bs[p-1]]) + (1<<(p-1));//此区间内在p-1位置上有需要的二进制位,就加上相应的值,并进入下一层
return trie_query(p-1, son[st][1-bs[p-1]], son[en][1-bs[p-1]]);//p-1位置上没有需要的二进制位,说明这种类型的数字不存在,只能进入另一种类型的数字中去递归查询
}
int main()
{
int n, m, x;
scanf("%d%d", &n, &m);
tot = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", &x);
for(int j = len-1; j >= 0; j--) bs[j] = 1 & (x >> j);
trie_insert(len, root[i-1], root[i]);
}
int l, r;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &l, &r);
for(int j = len-1; j >= 0; j--) bs[j] = !(1 & (x >> j));
l++, r++;
int ans = trie_query(len, root[l-1], root[r]);
printf("%d\n", ans);
}
return 0;
}
非递归版
#include
using namespace std;
const int N = 50000 + 10;
int son[N*35][2], sum[N*35];
int root[N];
int tot;
int len = 31;
bool bs[35];
//void trie_insert(int p, int pre, int &x)
//{
// x = ++tot;
// son[x][0] = son[pre][0], son[x][1] = son[pre][1];
// //memcpy(son[x], son[pre], sizeof(int) * 2);
// sum[x] = sum[pre] + 1;
// if(! p) return;
// trie_insert(p-1, son[pre][bs[p-1]], son[x][bs[p-1]]);
//}
//int trie_query(int p, int st, int en)
//{
// if(! p) return 0;
// if(sum[son[en][bs[p-1]]] > sum[son[st][bs[p-1]]]) return trie_query(p-1, son[st][bs[p-1]], son[en][bs[p-1]]) + (1<<(p-1));
// return trie_query(p-1, son[st][1-bs[p-1]], son[en][1-bs[p-1]]);/
//}
int trie_insert(int val, int pre)
{
int x = ++tot, t = x;
for(int i = len-1; i >= 0; i--)
{
son[x][0] = son[pre][0], son[x][1] = son[pre][1];
sum[x] = sum[pre] + 1;
int j = 1 & (val >> i);
son[x][j] = ++tot;
x = son[x][j], pre = son[pre][j];
}
sum[x] = sum[pre] + 1;
return t;
}
int trie_query(int val, int y, int x)
{
int ans = 0;
for(int i = len-1; i >= 0; i--)
{
int j = !(1 & (val >> i));
if(sum[son[x][j]] - sum[son[y][j]]> 0)
{
ans |= (1 << i);
x = son[x][j], y = son[y][j];
}
else x = son[x][!j], y = son[y][!j];
}
return ans;
}
int main()
{
int n, m, x;
scanf("%d%d", &n, &m);
tot = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", &x);
root[i] = trie_insert(x, root[i-1]);
}
int l, r;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &l, &r);
l++, r++;
int ans = trie_query(x, root[l-1], root[r]);
printf("%d\n", ans);
}
return 0;
}