HDU5536 Chip Factory Trie(01字典树)

题意:

给出 n(3n1000) n(3≤n≤1000)个数字,求 max(si+sj)sk max(si+sj)⨁sk,而且 i,j,k i,j,k互不相等。

分析:

把每个数字看成一个 01 01字符串插入倒Trie树中去,枚举 i i j j,然后把 si si sj sj从Trie树中删去。
然后在Trie树中贪心找到能与 si+sj si+sj异或得到的最大值。
具体匹配的过程中是这样的,首先看树中最高位能否异或得到 1 1
能的话就往能的那个方向走,否则往另外一个方向走。

另外删除操作是这样实现的,我们每个节点记录一个 val val值。
插入时对所有经过节点的 val val值加 1 1,删除就将对应节点的 val val值减 1 1
在树上匹配的时候就只走那些 val

val值为正的节点。


#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
const int maxnode = 1e5+10;
int sz;//总共节点个数
int s[maxn], ch[maxnode][2], val[maxnode];
void init()
{
    sz = 1;
    memset(ch[0], 0, sizeof(ch[0]));
}
//d=1表示插入,d=-1表示删除
void update(int v, int d)
{
    int u = 0;
    for(int i = 30; i >= 0; i--)
    {
        int c = (v >> i) & 1;
        if(!ch[u][c])
        {
            memset(ch[sz], 0, sizeof(ch[sz]));
            val[sz] = 0;
            ch[u][c] = sz++;
        }
        u = ch[u][c];
        val[u] += d;
    }
}
int match(int v)
{
    int ans = 0, u = 0;
    for(int i = 30; i >= 0; i--)
    {
        int c = (v >> i) & 1;
        if(ch[u][c^1] && val[ch[u][c^1]])
        {
            ans |= (1 << i);
            u = ch[u][c^1];
        }
        else
            u = ch[u][c];
    }
    return ans;
}
int main()
{
    int n, T;
    scanf("%d", &T);
    while(T--)
    {
        init();
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &s[i]);
            update(s[i], 1);
        }
        int ans = (s[1] + s[2]) ^ s[3];
        for(int i = 1; i <= n; i++)
        {
            update(s[i], -1);
            for(int j = i + 1; j <= n; j++)
            {
                update(s[j], -1);
                int t = match(s[i] + s[j]);
                ans = max(ans, t);
                update(s[j], 1);
            }
            update(s[i], 1);
        }
        printf("%d\n", ans);
    }
    return 0;
}


动态分配节点的写法:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
const int N = 2;
int s[maxn];
struct tree
{
    int val;
    tree *nxt[N];
    tree()
    {
        for(int i = 0; i < N; i++)
            nxt[i] = NULL;
        val = 0;
    }
}*root;
int a[33];
void ch(int x)
{
    memset(a, 0, sizeof(a));
    int len = 0;
    while(x)
    {
        a[len++] = x % 2;
        x /= 2;
    }
}
void Insert(int x)
{
    ch(x);
    int len = 31;
    tree *p = root;
    while(len >= 0)
    {
        int id = a[len];
        if(p -> nxt[id] == NULL)
        {
            tree *temp = new tree;
            p -> nxt[id] = temp;
        }
        p = p -> nxt[id];
        p -> val++;
        len--;
    }
}
int query(int x)
{
    ch(x);
    tree *p = root;
    for(int i = 0; i < 31; i++)
        a[i] ^= 1;
    int len = 31;
    int ans = 0;
    while(len >= 0)
    {
        int id = a[len];
        if(p -> nxt[id] == NULL || p -> nxt[id] -> val == 0)
            id ^= 1;
        if(id)
            ans +=  1 << len;
        p = p -> nxt[id];
        len--;
    }
    return ans ^ x;
}
void del(int x)
{
    ch(x);
    int len = 31;
    tree *p = root;
    while(len >= 0)
    {
        int id = a[len];
        p -> nxt[id] -> val--;
        p = p -> nxt[id];
        len--;
    }
}
void fre(tree *tmp)
{
    for(int i = 0; i < N; i++)
        if(tmp -> nxt[i])
            fre(tmp -> nxt[i]);
    delete(tmp);
}
int main()
{
    int n, T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        root = new tree;
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &s[i]);
            Insert(s[i]);
        }
        int ans = 0;
        for(int i = 0; i < n; i++)
        {
            del(s[i]);
            for(int j = i + 1; j < n; j++)
            {
                del(s[j]);
                ans = max(ans, query(s[i] + s[j]));
                Insert(s[j]);
            }
            Insert(s[i]);
        }
        printf("%d\n", ans);
        fre(root);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值