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

题目:

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值