本题链接:CF972 D. Kuro and GCD and XOR and SUM
题目大意
有(n)个询问,每个询问形如如下两种:
- 增加一个数(x)到一开始为空的序列中.
- 给定三个参数(x,k,s).要求在序列找一个数(v)满足(k | gcd(x,v)),(x + v leq s),(x oplus v)最大.如果不存在这样的(v)则输出(-1).
数据范围:
(1 leq q leq 10^5)
(1 leq x,k,s leq 10^5).
思路
显然(gcd)这个条件的约束性可比什么求和强多了,先对这玩意下手.注意到(k | gcd(x,v))事实上跟一般的条件不大一样,他其实只是满足:(k | x)以及(k | x)而已.所以对于每个询问可以先排除掉一种无解情况:即(x)不是(k)的倍数的时候.接下来其实只用找(k)的倍数(v)了.
不过有一点需要分开讨论:如果(k=1)此时的倍数太多了,所以我们不妨只讨论(k=1)的情况以及(k eq 1)的情况:
- 若(k=1),那么必然有(k|v),此时只需要找一个在序列里面的数(v)满足(x+vleq s)就可以满足有解的存在了,之后就需要满足(x oplus v)最大,这个可以用一个
Trie
来做,模型还是比较典型的插入数以及查询.那么为了满足不等式条件,我们只需要对每个在Trie
上的点额外打一个最小值标记表示在这个点下最小的值是多少,如果最小值标记都不能满足不等式关系,那么说明:在这个点之下无论在怎么去找都找不到一个合法的点了,只能尝试另外一边的点是否合法,如果都走不通就说明不存在点.贪心去找就可以了. - 枚举(z=k)的若干倍,满足(z+x leq s).在这种情况下,我们只需要查询在
Trie
上是否存在着这个(z)就可以了.更新最大的异或值,维护对应的答案即可.
(感觉复杂度有点玄幻)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
const int N = 1e5+7,M = 24;
int tr[N * M][2],idx,dwtag[N * M];
void insert(int v)
{
int p = 0;
for(int i = M;i >= 0;--i)
{
int k = v >> i & 1;
if(!tr[p][k]) tr[p][k] = ++idx;
p = tr[p][k];
dwtag[p] = min(v,(dwtag[p] == 0 ? v : dwtag[p]));
}
}
int query(int x,int limit)
{
int p = 0;
for(int i = M;i >= 0;--i)
{
int o = tr[p][x >> i & 1],r = tr[p][!(x >> i & 1)];
if(dwtag[o] + x > limit && dwtag[r] + x > limit) return -1;
if(dwtag[r] + x <= limit) p = r;
else p = o;
}
return dwtag[p];
}
int query(int x)
{
int p = 0;
for(int i = M;i >= 0;--i)
{
p = tr[p][x >> i & 1];
if(!p) return 0;
}
return p;
}
int main()
{
dwtag[0] = 1e9;
int q;scanf("%d",&q);
while(q--)
{
int t;scanf("%d",&t);
if(t == 1)
{
int x;scanf("%d",&x);
insert(x);
}
else
{
int x,k,s;scanf("%d%d%d",&x,&k,&s);
if(x % k) puts("-1");
else
{
if(k == 1) printf("%d
",query(x,s));
else
{
int resx = -1e9,resv = -1;
for(int z = k;x + z <= s;z += k)
if(query(z))
{
if((z ^ x) > resx)
{
resx = z ^ x;
resv = z;
}
}
printf("%d
",resv);
}
}
}
}
return 0;
}