2021hdu多校1 Xor sum(字典树)

**题意:**给你一串数组a,问连续区间异或和<=k的区间,输出这个区间的范围L,R,要求这个区间最小,如果区间相同,则输出L最小的那一个。
题解:

  1. 根据性质b^b=0,所以xor_sum[i,j] = xor_sum[1,i-1] ^ xor_sum[1,j],所以用前缀异或和进行维护。
  2. 前缀异或和,我们先在字典树进行查询当前1-i区间异或和,在查找的时候,我们进行判断是否大于等于K,然后存入最小的下标。
  3. 在查询过后,在将当前异或和插入到字典树中。这样就能完成O(nlogn)的全部扫描了。
    code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const double PI = acos(-1.0);
int n, k;

const int N = 100050;
int trie[N * 33][3], tot = 1;
int End[N * 33];

void insert_trie(ll x, int pos)
{
    int p = 1;
    for (int i = 30; i >= 0; i--)
    {
        int ch = (x >> i) & 1;
        if (trie[p][ch] == 0)
            trie[p][ch] = ++tot;
        p = trie[p][ch];
    }
    End[p] = pos; //标记编号
}

ll search_trie(ll x)
{
    ll res = 0;
    ll ans = -1;
    int p = 1;
    for (int i = 30; i >= 0; i--)
    {
        int ch = (x >> i) & 1;
        if (res + (1 << i) >= k && End[trie[p][ch ^ 1]]) //寻找最小的符合条件的编号
            ans = max(ans, (ll)End[trie[p][ch ^ 1]]);
        if (res >= k && End[trie[p][ch]])
            ans = max(ans, (ll)End[trie[p][ch]]);

        if (trie[p][ch ^ 1])
        {
            res += 1 << i;
            p = trie[p][ch ^ 1];
        }
        else
        {
            p = trie[p][ch];
        }
    }
    return ans;
}

void init()
{
    for (int i = 0; i <= n * 32 + 5; i++)
    {
        End[i] = 0;
        for (int j = 0; j <= 2; j++)
        {
            trie[i][j] = 0;
        }
    }
    tot = 1;
}

void solve()
{
    scanf("%d%d", &n, &k);
    init();

    ll tmp = 0;
    ll a;
    int len = 0x7f7f7f7f;
    int l = 0, r = 0;
    insert_trie(0, 1); //将0点进行插入

    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &a);
        tmp ^= a;

        int tmpl = search_trie(tmp);
        insert_trie(tmp, i + 1);
        if (tmpl != -1 && len > (i - tmpl + 1))
        {
            len = i - tmpl + 1;
            l = tmpl, r = i;
        }
    }

    if (len == 0x7f7f7f7f)
        printf("-1\n");
    else
        printf("%d %d\n", l, r);
}
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值