【字典树(删除操作)】Power OJ 2871

Power OJ 2871

题意:n个数里找到三个数,使得( a[ i ] + a[ j ] ) ^ a[ k ] 最大。(i, j, k互不相同)

思路:首先考虑到时间最大是 5s ,然后 n 最大是4000,所以我们最大的复杂度是n ^ 2 * log n,所以我们可以直接跑二层循环找相加的两个数,然后用字典树来找异或最大的数。

【字典树的删除操作】

因为我们要保证三个数不相同,所以我们要将a[ i ] 和 a[ j ]从字典树里删除掉,然后再找a[ k ]。

我们需要注意的就是删除操作怎么实现?

首先我们用一个cnt数组来存该结点有多少个子节点。如果我们直接从中间将树一分为二(也就是Delete的时候遇到 - - cnt[ ] 为0的时候就直接return,并且用tire[ ][ ] = 0 截断了这颗树,后面的结点不管),因为再次插入数值的时候tot是直接开辟新空间的,那么我们的空间消耗是很大的。

所以我们采用将所有该数值的32位所在的结点全部 cnt 减一,但是不截断这棵树。这样的话我们Insert的时候tot就不会额外的增加。需要注意的是查找的时候要判断一下tire[ rt ] [ id ^ 1 ] && cnt[ tire[ rt ] [ id ^ 1] ] > 0,树上有我们要找的数值,并且该结点存在(因为我们可能删除了还没有插入回来)。

这样的的话就解决了问题。

【关于初始化】

其实val是不需要初始化的,因为Insert的时候,会重新赋值并且Search的时候只会用到该次样例下赋过值的,所以之前用过的也不影响。
2731 MS 2000 KB

这里是每用一个tire 和 cnt 数组空间清零一个。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>

using namespace std;
typedef long long ll;
const int maxN= 2e5 ;

int n, a[maxN], tire[maxN][2], cnt[maxN], val[maxN], tot;

void init()
{
    tot = 0;
    cnt[0] = 0;
    for(int i = 0; i < 2 ; i++)
        tire[0][i] = 0;
}

void Clear(int rt)
{
    for(int i = 0; i < 2 ; i++)
        tire[rt][i] = 0;
    cnt[rt] = 0;
}

void Insert(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0 ; i --)
    {
        int id = (x >> i) & 1;
        if(!tire[rt][id])
        {
            tire[rt][id] = ++tot;
            Clear(tot);
        }
        cnt[ tire[rt][id] ] ++;
        rt = tire[rt][id];
    }
    val[rt] = x;
}

void Delete(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0; i -- )
    {
        int id = (x >> i) & 1;
        -- cnt[ tire[rt][id] ];
        rt = tire[rt][id];
    }
}

int Search(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0; i --)
    {
        int id = (x >> i) & 1;
        if(tire[rt][id ^ 1] && cnt[ tire[rt][id ^ 1] ] > 0)
            rt = tire[rt][id ^ 1];
        else
            rt = tire[rt][id];
    }
    return val[rt];
}

int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d", &a[i]);
            Insert(a[i]);
        }
        int ans = 0;
        for(int i = 1 ; i <= n ; i++)
        {
            Delete(a[i]);
            for(int j = i+1 ; j <= n ; j++)
            {
                int sum = a[i] + a[j];
                Delete(a[j]);
                int tmp = Search(sum);
                ans = max(ans, tmp ^ sum);
                Insert(a[j]);
            }
            Insert(a[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

2869 MS 3608 KB

这个就是不用memset,用多少n,初始化多少数组

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
 
using namespace std;
typedef long long ll;
const int maxN= 2e5 ;
 
int n, a[maxN], tire[maxN][2], cnt[maxN], val[maxN], tot;
 
void init()
{
    for(int i = 0 ; i<= 32 * n ; i++)
    {
        val[i] = 0;
        cnt[i] = 0;
        memset(tire[i], 0, sizeof(tire[i]));
    }
    tot = 0;
}
 
void Insert(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0 ; i --)
    {
        int id = (x >> i) & 1;
        if(!tire[rt][id])
            tire[rt][id] = ++tot;
        cnt[ tire[rt][id] ] ++;
        rt = tire[rt][id];
    }
    val[rt] = x;
}
 
void Delete(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0; i -- )
    {
        int id = (x >> i) & 1;
        -- cnt[ tire[rt][id] ];
        rt = tire[rt][id];
    }
}
 
int Search(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0; i --)
    {
        int id = (x >> i) & 1;
        if(tire[rt][id ^ 1] && cnt[ tire[rt][id ^ 1] ] > 0)
            rt = tire[rt][id ^ 1];
        else
            rt = tire[rt][id];
    }
    return val[rt];
}
 
int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d", &a[i]);
            Insert(a[i]);
        }
        int ans = 0;
        for(int i = 1 ; i <= n ; i++)
        {
            Delete(a[i]);
            for(int j = i+1 ; j <= n ; j++)
            {
                int sum = a[i] + a[j];
                Delete(a[j]);
                int tmp = Search(sum);
                ans = max(ans, tmp ^ sum);
                Insert(a[j]);
            }
            Insert(a[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

3022 MS 4720 KB

这个就是直接用memset初始化。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
 
using namespace std;
typedef long long ll;
const int maxN= 2e5 ;
 
int n, a[maxN], tire[maxN][2], cnt[maxN], val[maxN], tot;
 
void init()
{
    memset(val, 0, sizeof(val));
    memset(tire, 0, sizeof(tire));
    memset(cnt, 0, sizeof(cnt));
    tot = 0;
}
 
void Insert(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0 ; i --)
    {
        int id = (x >> i) & 1;
        if(!tire[rt][id])
            tire[rt][id] = ++tot;
        cnt[ tire[rt][id] ] ++;
        rt = tire[rt][id];
    }
    val[rt] = x;
}
 
void Delete(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0; i -- )
    {
        int id = (x >> i) & 1;
        -- cnt[ tire[rt][id] ];
        rt = tire[rt][id];
    }
}
 
int Search(int x)
{
    int rt = 0;
    for(int i = 31; i >= 0; i --)
    {
        int id = (x >> i) & 1;
        if(tire[rt][id ^ 1] && cnt[ tire[rt][id ^ 1] ] > 0)
            rt = tire[rt][id ^ 1];
        else
            rt = tire[rt][id];
    }
    return val[rt];
}
 
int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d", &a[i]);
            Insert(a[i]);
        }
        int ans = 0;
        for(int i = 1 ; i <= n ; i++)
        {
            Delete(a[i]);
            for(int j = i+1 ; j <= n ; j++)
            {
                int sum = a[i] + a[j];
                Delete(a[j]);
                int tmp = Search(sum);
                ans = max(ans, tmp ^ sum);
                Insert(a[j]);
            }
            Insert(a[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值