Atcoder ABC223 H - Xor Query
听说是原题,亏大了 /ll
Description
英文版题面:
Given is a sequence of \(N\) positive integers \(A=(A_1,A_2,...,A_N)\).
Process \(Q\) queries. In the i-th query \((1≤i≤Q)\), determine whether it is possible to choose one or more elements from \(A_{L_i},A_{L_i+1},...,A_{R_i}\) so that their \(\text{XOR}\) is \(X_i\)
中文版题面:
给你一个长度为 \(n\) 的序列 \(a\),\(Q\) 次询问,每次询问给你三个数 \(l,r,x\),问能否用序列中位置在 \([l,r]\) 内的数相互异或得到一个数 \(x\)。
\(n \le 4 \times 10^5, Q \le 2 \times 10^5, a_i \le 2^{60}\)
Solution
异或凑出一个数,显然需要线性基。
一开始考虑用线段树暴力维护区间线性基处理询问,即把每个数看做一个线性基,区间合并就是把两个区间的线性基插在一起,查询的时候用类似的思路就能得到 \([l,r]\) 区间内的数构成的线性基。
时间复杂度是 \(\mathcal O((n+q) \log^2 n \log V)\),线段树的 \(\log n\),\(\log n\) 个线性基合并,每次合并都是 \(\log V\)
你考虑一个更高效的做法。
查询的时候,对于每一个结束位置 \(r\),用这个位置前面尽可能靠后的数构成一个线性基。那么查询的时候只要在多判断一下构成这个数的所需的线性基的位置是不是都 \(\ge l\),如果出现一个 \(< l\) 那一定是不合法的了。
预处理的时候记录每个位置用的数的 \(id\),
从高位向低位扫描这个线性基,如果遇到更靠后的就和原来的 swap 一下,然后从下一位继续插入。
不难发现这个线性基有一个更好的性质,高位的数出现尽量靠后。(但这个性质没什么用)
时间复杂度 \(\mathcal O((n+Q) \log V)\)。
更多细节看代码吧,有问题也可以在评论区提出。
Code
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 4e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
struct LB {
int p[66], bel[66];
void Insert(int val, int id) {
for(int i = 60; i >= 0; --i) {
if(!(val & (1ll << i))) continue;
if(!p[i]) {
p[i] = val, bel[i] = id;
return ;
} else {
if(bel[i] < id) swap(bel[i], id), swap(p[i], val);
val ^= p[i];
}
}
}
bool Query(int val, int l) {
for(int i = 60; i >= 0; --i) {
if(!(val & (1ll << i))) continue;
if(!p[i] || bel[i] < l) return false;
val ^= p[i];
}
return true;
}
}b[MAXN];
int n, Q;
int a[MAXN];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
signed main()
{
n = read(), Q = read();
for(int i = 1; i <= n; ++i) {
a[i] = read();
b[i] = b[i - 1];
b[i].Insert(a[i], i);
}
for(int i = 1, l, r, x; i <= Q; ++i) {
l = read(), r = read(), x = read();
b[r].Query(x, l) ? puts("Yes") : puts("No");
}
return 0;
}