最近遇到的三道字典树问题

1:A + B Problem agin

数字A + 数字B不进位,对于每个数字求相加最大的结果。
字典树的异或的板子题是不进位求a[i] ^ a[j] 最大,这题也类似。只是多了一个删除数字的操作,因为要避免同一个数字加两次。
关于删除,只需要在插入的时候有计数的数组val[p]++,意味着当前这个节点有一个字符,删除的时候,val[p] += add,add为1或-1,就能实现这个删除之后又更新的操作了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5, mod = 998244353;
int tree[N * 10][10], a[N], val[N * 10];
int tot = 1;
void insert(int x)
{
    string s = to_string(x);
    while(s.length() < 5) s = "0" + s;
    int p = 1;
    for (auto i : s) {
        int num = i - '0';
        if (tree[p][num] == 0)
            tree[p][num] = ++tot;
        p = tree[p][num];
        val[p]++;
    }
}
int search(int x)
{
    string s = to_string(x);
    while(s.length() < 5) s = "0" + s;
    int p = 1;
    int ret = 0;
    for (auto i : s) {
        int num = i - '0';
        int ma = -1;
        for (int j = 9; j >= 0; j--) {
            if (tree[p][j] && val[tree[p][j]]) ma = max(ma, (num + j) % 10);
        }
        p = tree[p][((ma - num) % 10 + 10) % 10];
        ret = ret * 10 + ma;
    }
    return ret;
}
void up(int x, int add)
{
    string s = to_string(x);
    while(s.length() < 5) s = "0" + s;
    int p = 1;
    for (auto i : s) {
        int num = i - '0';
        p = tree[p][num];
        val[p] += add;
    }
}
void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        insert(a[i]);
    }
    for (int i = 1; i <= n; i++) {
        up(a[i], -1);
        printf("%lld ", search(a[i]));
        up(a[i], 1);
    }
}
signed main()
{
    int tt = 1;
    // cin >> tt;
    while (tt--) solve();
    return 0;
}

HDU5336:

HDU5336

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000 + 5, mod = 998244353;
int tree[N * 32][10], a[N], val[N * 32];
int tot = 1;
void insert(int x)
{
    int p = 1;
    for (int i = 31; i >= 0; i--) {
        int f = (x >> i) & 1;
        if (tree[p][f] == 0) tree[p][f] = ++tot;
        p = tree[p][f];
        val[p]++;
    }
}
int search(int x)
{
    int p = 1;
    int ret = 0;
    for (int i = 31; i >= 0; i--){
        int f = (x >> i) & 1;
        if (tree[f ^ 1] && val[tree[p][f ^ 1]]) {
            p = tree[p][f ^ 1];
            ret |= (1ll << i);
        }
        else p = tree[p][f];
    }
    return ret;
}
void up(int x, int add)
{
    int p = 1;
    for (int i = 31; i >= 0; i--) {
        int f = (x >> i) & 1;
        p = tree[p][f];
        val[p] += add;
    }
}
void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        insert(a[i]);
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) 
        {
            up(a[i], -1);
            up(a[j], -1);
            ans = max(ans, /* (a[i] + a[j]) ^  */search(a[i] + a[j]));
            up(a[i], 1);
            up(a[j], 1);
        }
    }
    cout << ans << "\n";

    for (int i = 1; i <= tot; i++) {
        tree[i][0] = tree[i][1] = 0;
        val[i] = 0;
    }
    tot = 1;
}
signed main()
{
    int tt = 1;
    cin >> tt;
    while (tt--) solve();
    return 0;
}

D2. 388535 (Hard Version)

给定一个区间LR之间的数异或x的序列,求x是多少。
做法:
枚举x,求x与这个序列的最大异或值和最小异或值,如果max == r && min == l,那么x是符合条件的。怎么枚举呢?可以枚举 a[i] ^ L,这样总能碰到那个x。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = (1<<17) + 5, mod = 998244353;
int tree[N * 17][10], a[N], val[N * 17];
int tot = 1;
void insert(int x)
{
    int p = 1;
    for (int i = 31; i >= 0; i--) {
        int f = (x >> i) & 1;
        if (tree[p][f] == 0) tree[p][f] = ++tot;
        p = tree[p][f];
        val[p]++;
    }
}
int search_max(int x)
{
    int p = 1;
    int ret = 0;
    for (int i = 31; i >= 0; i--){
        int f = (x >> i) & 1;
        if (tree[f ^ 1] && val[tree[p][f ^ 1]]) {
            p = tree[p][f ^ 1];
            ret |= (1ll << i);
        }
        else p = tree[p][f];
    }
    return ret;
}
int search_min(int x)
{
    int p = 1;
    int ret = 0;
    for (int i = 31; i >= 0; i--){
        int f = (x >> i) & 1;
        if (tree[f] && val[tree[p][f]]) {
            p = tree[p][f];
        }
        else {
            p = tree[p][f ^ 1];
            ret |= (1ll << i);
        }
    }
    return ret;
}
void up(int x, int add)
{
    int p = 1;
    for (int i = 31; i >= 0; i--) {
        int f = (x >> i) & 1;
        p = tree[p][f];
        val[p] += add;
    }
}
void solve()
{
    int l, r;
    cin >> l >> r;
    int n = r - l + 1;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        insert(a[i]);
    }

    for (int i = 1; i <= n; i++) {
        int ma = search_max(a[i] ^ l);
        int mi = search_min(a[i] ^ l);
        // printf("mi = %lld, ma = %lld\n", mi, ma);
        if (mi == l && ma == r) {
            cout << (a[i] ^ l) << "\n";
            break;
        }
    }
    

    for (int i = 1; i <= tot; i++) {
        tree[i][0] = tree[i][1] = 0;
        val[i] = 0;
    }
    tot = 1;
}
signed main()
{
    int tt = 1;
    cin >> tt;
    while (tt--) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值