一道融合题,考了挺多东西,是道好题目.
题目大意:
给你两种操作:
1.向集合中插入一个值 v v v
2.给出三个值 x , k , s x,k,s x,k,s.查询集合中满足以下条件的 v v v
① k ∣ g c d ( x , v ) k | gcd(x,v) k∣gcd(x,v) ② x + v ≤ s x + v \leq s x+v≤s ③ 最大化 x ⊕ v x\oplus v x⊕v
时间: 2000 m s 2000ms 2000ms,空间: 512 m b 512mb 512mb
题目思路:
看到条件③,自然想到01字典树去解决.给出 x x x,我们在字典树上贪心的往反方向走即可.
分析一下条件① :由于 g c d ( x , v ) gcd(x,v) gcd(x,v)要被k整除并且 g c d ( x , v ) gcd(x,v) gcd(x,v)一定是 x x x的约数.所以从[ x x x的因数]中找出[能被 k k k整除]的数.那么v一定是他们的倍数.
这里提出一个很妙(暴力)的方法:开 1 e 5 1e5 1e5颗字典树.每次插入数的时候.枚举其约数,插入到第[约数]个字典树里.那么在查询的时候,第 i i i颗字典树就存放了所有 i i i的倍数的值.
再来分析条件②: v ≤ s − x v\leq s-x v≤s−x,因为我们到时候是在字典树上贪心的往反方向走.所以我们可以维护一个区间最小值.若我们要走的这个节点中存在 m i n { v } ≤ s − x . min\{v\} \leq s-x. min{ v}≤s−x.那么就可以往这边走.
这个条件②等于是限制条件③的贪心…
时间复杂度: O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn) – 一个 l o g log log来自插入到字典树的复杂度.一个 l o g log log来自于我们认为一个数的约数平均有 l o g log log个.
空间复杂度: O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn).实际跑 420 m b 420mb 420mb
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int rt[maxn];
namespace Trie{
int tr[maxn * 400][2] , tot;
int mi[maxn * 400];
void add (int x , int id){
int u = rt[id];
mi[u] = min (mi[u] , x);
for (int i = 20 ; i >= 0 ; i--){
int v = ((x >> i) & 1);
if (!tr[u][v]) tr[u][v] = ++tot;
u = tr[u][v];
mi[u] = min (mi[u] , x);
}
}
int dfs (int u , int pos , int x , int d)
{
if (pos == -1) return 0;
int v = ((x >> pos) & 1);
if (mi[tr[u][!v]] <= d) return (1 << pos) + dfs(tr[u][!v] , pos - 1 , x , d);
if (mi[tr[u][v]] <= d) return dfs(tr[u][v] , pos - 1 , x , d);
return -1;
}
}
vector<int> fact[maxn];
void init ()
{
for (int i = 1 ; i < maxn ; i++)
for (int j = i ; j < maxn ; j += i)
fact[j].push_back(i);
}
int main()
{
memset (Trie::mi , 0x3f , sizeof Trie::mi);
init();
ios::sync_with_stdio(false);
int q; cin >> q;
for (int i = 1 ; i <= 1e5 ; i++)
rt[i] = ++Trie::tot;
while (q--){
int t;cin >> t;
if (t == 1){
int x; cin >> x;
for (auto g : fact[x]) Trie::add(x , g);
}else {
int x , k , s; cin >> x >> k >> s;
int d = s - x;
if (d < 1) {
printf("%d\n" , -1);
continue;
}
int ans = -1;
for (auto g : fact[x])
if (g % k == 0)
ans = max (ans , Trie::dfs(rt[g] , 20 , x , d));
if (ans == -1) {
printf("%d\n" , -1);
continue;
}
printf("%d\n" , ans ^ x);
}
}
return 0;
}