CodeForces - 482D Kuro and GCD and XOR and SUM(01字典树)

链接:CodeForces - 482D Kuro and GCD and XOR and SUM

题意:

给一个空的集合 a a a,共有 q    ( 2 ≤ q ≤ 1 0 5 ) q\;(2\le q\le 10^5) q(2q105)次操作,分为以下 2 2 2种操作

  • 1    u i 1\;u_i 1ui:将 u i u_i ui加入到集合 a a a    ( 1 ≤ u i ≤ 1 0 5 ) \;(1\le u_i\le 10^5) (1ui105)
  • 2    x i    k i    s i 2\;x_i\;k_i\;s_i 2xikisi:在集合 a a a中找到一个数 v v v,满足 k i ∣ gcd ⁡ ( x i , v ) ,    x i + v ≤ s i k_i|\gcd(x_i,v),\;x_i+v\le s_i kigcd(xi,v),xi+vsi x i ⊕ v x_i\oplus v xiv最大,并输出 v v v,若找不到,则输出 − 1    ( 1 ≤ x i , k i , s i ≤ 1 0 5 ) -1\;(1\le x_i,k_i,s_i\le10^5) 1(1xi,ki,si105)


分析:

首先第一个条件 k i ∣ gcd ⁡ ( x i , v ) k_i|\gcd(x_i,v) kigcd(xi,v),也就是要求满足 k i ∣ x i k_i|x_i kixi k i ∣ v k_i|v kiv,所以若不满足 k i ∣ x i k_i|x_i kixi,可以直接输出 − 1 -1 1

后两个条件: x i + v ≤ s i x_i+v\le s_i xi+vsi x i ⊕ v x_i\oplus v xiv最大,可以在字典树上进行查找,为保证 x i + v ≤ s i x_i+v\le s_i xi+vsi(即 v ≤ s i − x i v\le s_i-x_i vsixi),可以再 设置一个变量 m i n _ v a l [ u ] min\_val[u] min_val[u],表示以 u u u为根结点的子树内存储的最大值

这样每次查询 即要尽可能与 x i x_i xi异或最大,还要保证对应子树的 m i n _ v a l [ v ] ≤ s i − x i min\_val[v]\le s_i-x_i min_val[v]sixi


其次,为满足 k i ∣ v k_i|v kiv,可以 对每一个 k i k_i ki值建一个01字典树(存储所有 k i k_i ki的倍数,即能被 k i k_i ki整除的数),对于新输入的 u i u_i ui,要把 u i u_i ui插入到其所有因数对应的树中,找到所有因数的时间只需要 O ( u i ) O(\sqrt {u_i}) O(ui )



以下代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1e5+50;
const int INF=0x3f3f3f3f;
const int max_base=20;
int ch[maxn*200][2],val[maxn*200],min_val[maxn*200],tot;
struct bit_tire
{
    int root;
    void init()
    {
        ch[tot][0]=ch[tot][1]=0;
        val[tot]=0;
        min_val[tot]=INF;
        root=tot++;
    }
    void insert(int x)
    {
        int u=root;
        for(int i=max_base;i>=0;i--)
        {
            min_val[u]=min(x,min_val[u]);
            int c=(x>>i)&1;
            if(!ch[u][c])
            {
                ch[tot][0]=ch[tot][1]=0;
                val[tot]=0;
                min_val[tot]=INF;
                ch[u][c]=tot++;
            }
            u=ch[u][c];
        }
        min_val[u]=val[u]=x;
    }
    int query_max(int x,int y)   //查询该tire小于等于y的与x异或最大值
    {
        int u=root;
        for(int i=max_base;i>=0;i--)
        {
            int c=(x>>i)&1;
            if(ch[u][c^1]&&min_val[ch[u][c^1]]<=y)
                u=ch[u][c^1];
            else if(ch[u][c]&&min_val[ch[u][c]]<=y)
                u=ch[u][c];
            else
                return -1;
        }
        return val[u];
    }
}t[maxn];
void init()
{
    tot=1;
    for(int i=1;i<maxn;i++)
        t[i].init();
}
int main()
{
    init();
    int q,op,u,x,k,s;
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d",&u);
            int k=(int)round(sqrt(u));
            for(int i=1;i<=k;i++)
            {
                if(u%i==0)
                {
                    t[i].insert(u);
                    t[u/i].insert(u);
                }
            }
        }
        else
        {
            scanf("%d %d %d",&x,&k,&s);
            if(x%k!=0)
                printf("-1\n");
            else
                printf("%d\n",t[k].query_max(x,s-x));
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值