51nod 1295 XOR key 01trie(可持久化字典树坑已填)

来源:互联网 发布:suse 11 yum 编辑:程序博客网 时间:2024/05/17 00:51

题目:

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 <bits/stdc++.h>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 <bits/stdc++.h>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 <bits/stdc++.h>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;}
原创粉丝点击