D. Kuro and GCD and XOR and SUM
字典树真好玩。。。
牛老板提供的思路:建1e5个 字典树,每个数插入到以它的因子为根所在的字典树中,这样就实现了整除,当然gcd(k, x) = k是必须的
然后如何保证v + x <= s 和 v ^ x 最大呢?
对于v + x <= s,我们可以维护01字典树中,经过每个节点的最小值,这样我们在访问每个节点时,直接min + x <= s 判断是否成立即可, 不成立就不用往下走了,因为最小值都不成立。
对于v ^ x最大,就是一般字典树思路了,~x与字典树比较选相同的位走,并且同时判断不等式条件就可以啦。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 50; struct node { int next[2]; int v; }; node tree[maxn * 200]; int root[maxn]; int sz = 1; void build(int p, int x) { if(!root[p]) root[p] = sz++; int tmp = root[p]; for(int i = 20; i >= 0; i--) { int id = (x >> i) & 1; if(tree[tmp].next[id] == 0) { memset(tree[sz].next, 0, sizeof(tree[sz].next)); tree[sz].v = 1e6; tree[tmp].next[id] = sz++; } tmp = tree[tmp].next[id]; tree[tmp].v = min(tree[tmp].v, x); } } void match(int k, int s, int x) { int par = root[k]; if(par == 0) { printf("-1 "); return; } int fx = ~x; // printf("%d ", ((fx >> 1) & 1)); int ans = 0; int flag = 0; for(int i = 20; i >= 0; i--) { int id = (fx >> i) & 1; if(tree[par].next[id] && (tree[tree[par].next[id]].v + x) <= s) ///维护最小值,表示最少存在这样的解 { ans = tree[tree[par].next[id]].v; par = tree[par].next[id]; } else if(tree[par].next[1 - id] && (tree[tree[par].next[1 - id]].v + x) <= s) { ans = tree[tree[par].next[1 - id]].v; par = tree[par].next[1 - id]; } else { flag = 1; break; } } if(flag || (!ans)) { printf("-1 "); } else { printf("%d ", ans); } } int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } int main() { memset(root, 0, sizeof(root)); int q; scanf("%d", &q); while(q--) { int t; scanf("%d", &t); if(t == 1) { int u; scanf("%d", &u); for(int i = 1; i * i <= u; i++) { if(u % i == 0) ///i是u的因子 { build(i, u); build(u / i, u); } } } else { int x, k, s; scanf("%d %d %d", &x, &k, &s); int g = gcd(k, x); if(g != k) { printf("-1 "); } else { match(k, s, x); } } } }