字典树,数据结构-CF979D

一道融合题,考了挺多东西,是道好题目.

题目大意:

给你两种操作:
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) kgcd(x,v) x + v ≤ s x + v \leq s x+vs ③ 最大化 x ⊕ v x\oplus v xv

时间: 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 vsx,因为我们到时候是在字典树上贪心的往反方向走.所以我们可以维护一个区间最小值.若我们要走的这个节点中存在 m i n { v } ≤ s − x . min\{v\} \leq s-x. min{ v}sx.那么就可以往这边走.

这个条件②等于是限制条件③的贪心…

时间复杂度: 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;
}

猜你喜欢

转载自blog.csdn.net/qq_35577488/article/details/109089653