[lnsyoj2246/luoguCF979D]Kuro and GCD and XOR and SUM
题意
给定集合 \(S\),初始为空,进行 \(q\) 次修改或查询操作:修改操作将 \(x\) 加入集合;查询操作给定 \(x,s,k\),要求找到满足
\[\max_{u\in S,u+x\le s,k|\gcd(u,x)} \{u\oplus x\}
\]
的最小的 \(u\)。
sol
集合、异或、可查可改,可以自然地想到 0/1-Trie。
我们假设 \(k=1\),此时不需要考虑 \(k|\gcd(u,x)\) 的条件,这就是一个有上限的最大异或和问题。我们对每个点记录一个 \(minn\),表示经过这个点的最小值,这样,当找到一个点时,如果 \(s-x>minn_u\),就不能够进入这个点。
再来考虑 \(k\),我们可以对于每一个可能的 \(k\) 值都建立一棵 Trie 树,这样,每次插入的时候,\(O(\sqrt{n})\) 地枚举所有约数代表的 Trie 树,在这些 Trie 树中都插入 \(x\)。查询的时候直接查找第 \(k\) 棵树即可。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_set>
using namespace std;
const int N = 50000005, K = 20;
int q;
int tr[N][2], minn[N], idx;
unordered_set<int> st;
void ins(int x, int p){
for (int c = K - 1; c >= 0; c -- ){
int u = (x >> c) & 1;
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
minn[p] = minn[p] ? min(minn[p], x) : x;
}
}
int query(int x, int s, int k){
int res = 0, p = k;
for (int c = K - 1; c >= 0; c -- ){
int u = ((x >> c) & 1) ^ 1;
int ne = tr[p][u], xne = tr[p][u ^ 1];
if (ne && minn[ne] <= s - x) res += u << c, p = ne;
else if (xne && minn[xne] <= s - x) res += (u ^ 1) << c, p = xne;
else return -1;
}
return res ? res : -1;
}
int main(){
idx = 100000;
scanf("%d", &q);
while (q -- ){
int op, x;
scanf("%d%d", &op, &x);
if (op == 1) {
if (st.find(x) == st.end()){
for (int i = 1; i * i <= x; i ++ ){
if (x % i) continue;
ins(x, i);
if (i * i != x) ins(x, x / i);
}
st.insert(x);
}
}
else {
int k, s;
scanf("%d%d", &k, &s);
if (x % k) puts("-1");
else printf("%d\n", query(x, s, k));
}
}
return 0;
}