题目地址:https://codeforces.com/contest/1100/problem/F

题目:


多次查询区间的最大异或值,但是数据范围比较大,5e5个数,5e5个查询

 

解题思路:


用线段树维护区间线性基,果然T了,T在第19个test

感觉这道题其实和https://blog.csdn.net/Cassie_zkq/article/details/96448531的做法有点类似,对于区间查询都是先离线处理,然后区间的右边界一定,依次寻找答案

参考网上的代码,这道题可以不用对右边界排序,存储方法比较巧妙。

对于每个行阶梯矩阵的每一行,采用后入为主的思想,如果j行已经有数了,那么就让新的数取代原来的数,让新的数和原来的数异或,之后再向后位查询可以放置的位置,并记录行阶梯矩阵每行的pos值,即若该行的值是由a[i],a[k]..a[m]异或出来的,让pos=最小的下标。

在右边界一定的前提下,统计每个区间的最大异或值

 

ac代码:


#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
const int max_base = 20;
int n, q, l, r;
int a[maxn], val[max_base+3], pos[max_base+3], ans[maxn];
vector<pair<int, int> > R[1000005];//R[i]存右边界为i的区间,key表示左边界,value表示对应的编号
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    cin >> q;
    for(int i = 1; i <= q; i++)
    {
        scanf("%d %d", &l, &r);
        R[r].push_back(make_pair(l, i));
    }
    for(int i = 1; i <= n; i++)//右边界为i
    {
        int x = a[i], p = i;
        for(int j = max_base; j >= 0 ;j--)
        {
            if(x>>j&1)
            {
                if(!val[j])//填入
                {
                    val[j] = x;
                    pos[j] = p;
                    break;
                }
                if(pos[j] < p) swap(val[j], x), swap(pos[j], p);
                x ^= val[j];
            }
        }
        for(int j = 0; j < R[i].size(); j++)
        {
            for(int k = max_base; k >= 0; k--)
                if(R[i][j].first <= pos[k] && val[k])
                    ans[R[i][j].second] = max(ans[R[i][j].second], ans[R[i][j].second]^val[k]);
        }
    }
    for(int i = 1; i <= q; i++)
        cout << ans[i] << endl;
    return 0;
}