[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;
}
posted @ 2024-08-09 21:21  是一只小蒟蒻呀  阅读(16)  评论(0编辑  收藏  举报