codeforces 979D(01字典树)
图片来自此博主%%%
感觉博主说的很有道理
代码来自此博主
↓↓↓↓↓
%%%
↑↑↑↑↑讲解的也挺好
接下来就说说蒟蒻的理解吧
题意:
T次操作
ti=1就将num插入数列(初始数列为空)
ti=2 输入三个数xi,ki,si
就去找当前的数列中是否存在一个数v满足以下条件:
① 使得GCD(xi,v)%ki==0
② xi+v<=si
③ 尽可能使xi异或v的值最大
———————————————————
思路:
(01字典树求最大异或值就不赘述了)
将1到1e5所有的数的约数都放进字典树T[k],在建树的时候,当前节点的值为min(val[u],x)
(这里不太懂。。)
查询的时候就是从最大的值往下去找(这样就是两个数的GCD)去找尽可能使xi异或v的值最大,并且满足条件②
好像是这么一回事。。。。
———————————————————
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=100050;
struct Trie{
int ch[maxn*400][2];
int T[maxn],sz,val[maxn*400];
Trie(){
sz=0;
for(int i = 1; i < maxn; ++i) T[i]=++sz;//节点初始化
}
void insert(int k,int x){
int u = T[k];
for(int i = 19; i >= 0; --i){
int c=(x>>i)&1;
if(!ch[u][c]) ch[u][c]=++sz,u=sz,val[u]=x;
else u=ch[u][c],val[u]=min(val[u],x);
}
}
int query(int x,int k,int s){
if(x%k) return -1;
int u=T[k];//当前数k的起始节点
for(int i = 19; i >= 0; --i){
int c=((x>>i)&1^1);
if(ch[u][c]&&val[ch[u][c]]<=s-x) u=ch[u][c];
else u=ch[u][c^1];
}
if(val[u]>s-x||val[u]==0) return -1;
return val[u];
}
}T;
vector<int> v[maxn];
bool vis[maxn];
int main(){
for(int i = 1; i < maxn; ++i){
for(int j = i; j < maxn; j+=i){
v[j].push_back(i);//将1~1e5的每个数的约数都放进vector
//v[j][i]代表j%i==0
//v[j]则为数j在1~1e5的所有约数
}
}
int q;
scanf("%d", &q);
while(q--){
int t,x,k,s;
scanf("%d", &t);
if(t&1){
scanf("%d", &x);
if(vis[x]) continue;
for(auto c:v[x]) T.insert(c,x);//相当于java的foreach
}
else{
scanf("%d%d%d", &x,&k,&s);
printf("%d\n", T.query(x,k,s));
}
}
}
——————————————————————————————
新技能get√
-
for(int i = 1; i < maxn; ++i) for(int j = i; j < maxn; j+=i){ v[j].push_back(i);//将1~1e5的每个数的约数都放进vector //v[j][i]代表j%i==0 //v[j]则为数j在1~maxn-1的所有约数 }