CodeForces 979 D.Kuro and GCD and XOR and SUM (01字典树*n)

题意:

q次操作:
(1,x)加入x
(2,x,k,s)查询

查询操作:
在已有的数中找到一个数v,满足k∣GCD(x,v),且x+v<=s,同时还要求x异或v的值最大

输出每次查询的结果v,如果找不到满足条件的数就输出-1

数据范围:所有数据<=1e5

思路:

k∣GCD(x,v)意味着gcd(x,v)%k=0,则题目条件转化为x%k=0且v%k=0

如果单是异或最大值可以用01字典树解决。
处理x%k=0且v%k=0:
建立1e5棵字典树,对于每棵树k,只存k的倍数,每次加入x,把x的因子对应字典树都插入x
查询则到k树中找和x异或最大的v就行了,因为k树中只有k的倍数,所以找到的v一定满足v%k=0
注意还有一种情况是x%k!=0,这种情况直接输出-1不用找了。

因为还需要满足x+v<=s,查询操作中有的路径是不能走的,因为可能走之后找到的v一定不满足这个条件,因此插入的时候更新路径上x的最小值,查询的时候就可以判断走某条路径是否满足该条件。

code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
const int maxd=17;//1e5最多17位
struct Node{
    int a[maxm*maxd*15][2],tot;
    int rt[maxm];
    int val[maxm*maxd*15];
    void init(){
        tot=0;
        for(int i=1;i<maxm;i++){
            rt[i]=++tot;
        }
    }
    void add(int x,int k){//把x插入k树下
        int node=rt[k];
        for(int i=maxd;i>=0;i--){
            int v=(x>>i&1);
            if(!a[node][v]){
                a[node][v]=++tot;
                node=a[node][v];
                val[node]=x;
            }else{
                node=a[node][v];
                val[node]=min(val[node],x);//因为需要满足x+val<=s,路径取min
            }
        }
        val[node]=x;
    }
    int ask(int x,int k,int s){//查询
        if(x%k)return -1;//这个不加会wa84,题目不保证x%k==0
        int node=rt[k];
        for(int i=maxd;i>=0;i--){
            int v=(x>>i&1);
            if(a[node][v^1]&&x+val[a[node][v^1]]<=s){//走满足条件的
                node=a[node][v^1];
            }else{//
                node=a[node][v];
            }
        }
        if(x+val[node]>s||val[node]==0)return -1;
        return val[node];
    }
}t;
vector<int>fac[maxm];
signed main(){
    for(int i=1;i<maxm;i++){//预处理每个数的因子
        for(int j=i;j<maxm;j+=i){
            fac[j].push_back(i);
        }
    }
    t.init();//不要忘了初始化
    int q;
    cin>>q;
    while(q--){
        int d;
        cin>>d;
        if(d==1){//add
            int x;
            cin>>x;
            for(int v:fac[x]){//每个因子的字典树都添加一次
                t.add(x,v);
            }
        }else{//d==2,ask
            int x,k,s;
            cin>>x>>k>>s;
            int ans=t.ask(x,k,s);//在k树下找答案
            cout<<ans<<endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值