51nod1577 异或凑数(算法马拉松20) 特殊的线性基构造方法

题目大意

从左到右一共 n 个数,数字ai下标从 1 n编号。
一共 m 次询问,每次询问是否能从第L个到第 R 个数中(包括第L个和第 R 个数)选出一些数使得他们异或为K

n5105
ai,K<230
m5105

解题思路

求一堆数能否异或成 k ,一个很显然的思路就是构出这堆数的线性基,然后看一下能不能用线性基里的数表示k

那么我们可以以序列中的每个位置为开头,最后一个位置为结尾构造线性基,并且线性基中的每个位置额外存下这个数在序列中的位置。判断 k 的时候就把k在线性基中跑,当失配或者当前值对应序列中的位置大于r时就意味这不合法。

现在的问题是如何快速构造这 n 个线性基。考虑从后往前构造。假设当前已经构好了后i个数的线性基,要把第i个数插进去。首先让ai在当前的线性基中跑,遇到已经有数的位置时就比较当前数和这个位置的数的位置关系。把位置较前的数放进去,位置较后的数拿出来继续跑,这样就能令构造出来的线性基与从前往后一个个插入的效果一样,并且是 O(nlogn) 的。

具体实现看程序!

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>

using namespace std;

const int MAXN = 5e5 + 5;

struct Node {
    int num, side;
    Node (int a, int b) {num = a, side = b;}
    Node () {}
};

Node f[MAXN][30];
int n, m, a[MAXN];

void read(int &x) {
    char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    x = 0;
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}

int main() {
    read(n);
    for (int i = 1; i <= n; i ++) read(a[i]);
    for (int i = n; i; i --) {
        memcpy(f[i], f[i + 1], sizeof f[i]);
        Node now = Node(a[i], i);
        for (int j = 29; j + 1; j --) {
            if (!(now.num & (1 << j))) continue;
            if (!f[i][j].num) {swap(f[i][j], now); break;} else {
                if (now.side < f[i][j].side) swap(f[i][j], now);
                now.num ^= f[i][j].num;
            }
        }
    }
    read(m);
    for (int i = 1; i <= m; i ++) {
        int l, r, k;
        read(l), read(r), read(k);
        for (int j = 29; j + 1; j --) {
            if (!(k & (1 << j))) continue;
            if (f[l][j].num == 0 || f[l][j].side > r) break;
            k ^= f[l][j].num;
        }
        if (!k) puts("YES"); else puts("NO");
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值