[51nod 1577] 异或凑数

从左到右一共n个数,数字下标从1到n编号。

一共m次询问,每次询问是否能从第L个到第R个数中(包括第L个和第R个数)选出一些数使得他们异或为K。

数据量比较大。

输入请用快速读入

输出请用puts

Input
单组测试数据。
第一行一个整数n(0<n<=500,000)。
第二行n个整数,0<每个数<2^30。
第三行一个数m,表示询问次数(0<m<=500,000)。
接下来m行每行三个数,L,R,K(0<L<=R<=n,0<K<2^30)。
Output
M行,每行为YES或NO
Input示例
5
1 1 2 4 6
3
1 2 1
2 4 8
3 5 7
Output示例
YES
NO
NO




异或的问题当然是线性基了。
但是如何维护区间内的线性基呢?
线段树不太现实, 因为线性基只能暴力合并,再加上线段树就是三个log肯定炸了。
我们考虑一个以l为左端点的线性基, 并且每一个基存一下它出现的位置。
从后往前跑, 已经构造了i以后的线性基, 正在插入i。
如果线性基中他的位置存在一个数,就比较一下这个数的位置和i的大小关系,位置靠前的插入线性基, 位置考后的接着在线性基里跑。
复杂度O(NlongN)。

 
#include <iostream>
#include <cstdio>
using namespace std;
#define reg register
inline int read() {
    int res=0;char ch=getchar();bool flag=0;
    while(!isdigit(ch)) {if(ch=='-')flag=1;ch=getchar();}
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
    return flag ? -res : res;
}
int n, a[500005];
struct Lbase {
    int x, id;
}f[500005][31];

int main()
{
    n = read();
    for (reg int i = 1 ; i <= n ; i ++) a[i] = read();
    for (reg int i = n ; i >= 1 ; i --)
    {
        memcpy(f[i], f[i+1], sizeof f[i]);
        Lbase now = (Lbase){a[i], i};
        for (reg int j = 30 ; j >= 0 ; j --)
        {
            if (now.x >> j)
            {
                if (!f[i][j].x) swap(now, f[i][j]);
                else {
                    if (f[i][j].id > now.id) swap(now, f[i][j]);
                    now.x ^= f[i][j].x;
                }
            }
        }
    }
    int T = read();
    while(T--)
    {
        int l = read(), r = read();
        int k = read();
        for (reg int i = 30 ; i >= 0 ; i --)
        {
            if ((k >> i) & 1)
            {
                if (f[l][i].id <= r and f[l][i].x)
                    k ^= f[l][i].x;
            }
        }
        if (!k) puts("Yes");
        else puts("No");
    }
    return 0;
}




原文地址:https://www.cnblogs.com/BriMon/p/9536452.html