Codeforces Round #482 (Div. 2) D. Kuro and GCD and XOR and SUM(Trie树,二进制)

描述

Kuro is currently playing an educational game about numbers. The game
focuses on the greatest common divisor (GCD), the XOR value, and the
sum of two numbers. Kuro loves the game so much that he solves levels
by levels day by day.

Sadly, he’s going on a vacation for a day, and he isn’t able to
continue his solving streak on his own. As Katie is a reliable person,
Kuro kindly asked her to come to his house on this day to play the
game for him.

Initally, there is an empty array aa. The game consists of qq tasks of
two types. The first type asks Katie to add a number uiui to aa. The
second type asks Katie to find a number vv existing in aa such that
ki∣GCD(xi,v)ki∣GCD(xi,v), xi+v≤sixi+v≤si, and xi⊕vxi⊕v is maximized,
where ⊕⊕ denotes the bitwise XOR operation, GCD(c,d)GCD(c,d) denotes
the greatest common divisor of integers cc and dd, and y∣xy∣x means xx
is divisible by yy, or report -1 if no such numbers are found.

Since you are a programmer, Katie needs you to automatically and
accurately perform the tasks in the game to satisfy her dear friend
Kuro. Let’s help her!

Input

The first line contains one integer qq (2≤q≤1052≤q≤105) — the number
of tasks the game wants you to perform.

qq lines follow, each line begins with an integer titi — the type of
the task:

  • If ti=1ti=1, an integer uiui follow (1≤ui≤1051≤ui≤105) — you have to add uiui to the array aa.
  • If ti=2ti=2, three integers xixi, kiki, and sisi follow (1≤xi,ki,si≤1051≤xi,ki,si≤105) — you must find a number vv existing in
    the array aa such that ki∣GCD(xi,v)ki∣GCD(xi,v), xi+v≤sixi+v≤si, and
    xi⊕vxi⊕v is maximized, where ⊕⊕ denotes the XOR operation, or report
    -1 if no such numbers are found.

It is guaranteed that the type of the first task is type 11, and there
exists at least one task of type 22.

Output

For each task of type 22, output on one line the desired number vv, or
-1 if no such numbers are found.

input

5
1 1
1 2
2 1 1 3
2 1 1 2
2 1 1 1

output

2
1
-1

input

10
1 9
2 9 9 22
2 3 3 18
1 25
2 9 9 20
2 25 25 14
1 20
2 26 26 3
1 14
2 20 20 9

output

9
9
9
-1
-1
-1

Note

In the first example, there are 5 tasks:

  • The first task requires you to add 11 into aa. aa is now {1}{1}.
  • The second task requires you to add 22 into aa. aa is now {1,2}{1,2}.
  • The third task asks you a question with x=1x=1, k=1k=1 and s=3s=3. Taking both 11 and 22 as vv satisfies 1∣GCD(1,v)1∣GCD(1,v) and
    1+v≤31+v≤3. Because 2⊕1=3>1⊕1=02⊕1=3>1⊕1=0, 22 is the answer to this
    task.
  • The fourth task asks you a question with x=1x=1, k=1k=1 and s=2s=2. Only v=1v=1 satisfies 1∣GCD(1,v)1∣GCD(1,v) and 1+v≤21+v≤2, so 11 is
    the answer to this task.
  • The fifth task asks you a question with x=1x=1, k=1k=1 and s=1s=1. There are no elements in aa that satisfy the conditions, so we report
    -1 as the answer to this task.

思路

首先说题意,题目有n个操作,操作1代表往集合里面加入一个数字x,操作2会给出三个数字x,k,s,然后对于每一个2操作,题目有一个询问,要求从集合中找一个数 v v 满足下面的条件:

  1. gcd(x,v)%k==0 g c d ( x , v ) % k == 0
  2. x+vs x + v ≤ s
  3. 使 xv x ⊕ v 的值最大

题目让从现在的a集合中找出满足上面这三个条件的数。

首先我们可以想到第一个条件 gcd(x,v)%k==0 g c d ( x , v ) % k == 0 ,那么必然有 x%k==0 x % k == 0 v%k==0 v % k == 0 ,那么我们可以

  1. 首先判断一下 x%k x % k 如果这个值不为0,那么一定输出-1
  2. 其次,我们很容易的想到01字典树的用法,我们利用字典树可以很容易的从中找出一堆数里面于一个数异或值最大的数(0找1,1找0),所以我们当 k==1 k == 1 的时候 gcd(x,v)%k==0 g c d ( x , v ) % k == 0 这个条件一定满足,所以问题转化成了我们从集合中找出小于等于s-x范围内的的异或值最大的数,我们用minn[]数组标记字典树中当前节点插入进来的最小值,对于普通的01字典树求异或最大值稍作改变即可
  3. 对于 k!=1 k ! = 1 的时候,因为 v v 一定是 k k 的倍数,所以我们可以在 sx s − x 范围内枚举 k k 的倍数,然后一直更新答案即可。

还有一种做法是利用set,详见代码2,这道题标程是字典树解法

代码1

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#include <list>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define inf 0x3f3f3f3f
typedef long long ll;
const int N = 2e5 + 10;
int a[N];
struct dicTree
{
    struct node
    {
        int next[2];
    } tree[N * 32];
    int minn[N]; //记录当前节点插入进来的最小的值
    int root, sz;
    int newnode()
    {
        tree[sz].next[0] = tree[sz].next[1] = -1;
        sz++;
        return sz - 1;
    }
    void init()
    {
        sz = 0;
        mem(minn, inf);
        root = newnode();
    }
    void insert(int x)
    {
        int p = x;
        int now = root;
        minn[now] = min(minn[now], p);
        for (int i = 31; i >= 0; i--)
        {
            int to = (x >> i) & 1;
            if (tree[now].next[to] == -1)
                tree[now].next[to] = newnode();
            now = tree[now].next[to];
            minn[now] = min(minn[now], p);
        }
    }
    int find(int x, int p)
    {
        int now = root;
        if (minn[now] > p)
            return -1;
        for (int i = 31; i >= 0; i--)
        {
            int to = (x >> i) & 1;
            if (tree[now].next[to ^ 1] != -1 && minn[tree[now].next[to ^ 1]] <= p)
                to ^= 1;
            now = tree[now].next[to];
        }
        return minn[now];
    }
} ac;
int main()
{
    //freopen("in.txt", "r", stdin);
    int t, op, x, k, s;
    ac.init();
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &op);
        if (op == 1)
        {
            scanf("%d", &x);
            a[x] = 1;
            ac.insert(x);
        }
        else
        {
            scanf("%d%d%d", &x, &k, &s);
            if (x % k != 0)
            {
                puts("-1");
                continue;
            }
            if (k == 1)
            {
                printf("%d\n", ac.find(x, s - x));
                continue;
            }
            int maxx = -1, ans = -1;
            for (int v = k; v <= s - x; v += k)
                if (a[v] && (v ^ x) > maxx)
                {
                    maxx = v ^ x;
                    ans = v;
                }
            printf("%d\n", ans);
        }
    }
    return 0;
}

代码2

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#include <list>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define inf 0x3f3f3f3f
typedef long long ll;
const int N = 1e5 + 10;
set<int> se[N];
int main()
{
    // freopen("in.txt", "r", stdin);
    int n, op, x, k, s;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &op);
        if (op == 1)
        {
            scanf("%d", &x);
            for (int i = 1; i <= (int)sqrt(x); i++)
            {
                if (x % i == 0)
                {
                    se[i].insert(x);
                    se[x / i].insert(x);
                }
            }
        }
        else
        {
            scanf("%d%d%d", &x, &k, &s);
            int sum = -1, ans = -1;
            if (x % k)
            {
                printf("%d\n", ans);
                continue;
            }
            auto it = se[k].upper_bound(s - x);
            if (se[k].empty() || it == se[k].begin())
            {
                printf("%d\n", ans);
                continue;
            }
            --it;
            for (; it != se[k].begin(); --it)
            {
                int v = *it;
                if (sum > x + v)
                    break;
                if (sum < (x ^ v))
                {
                    ans = v;
                    sum = x ^ v;
                }
            }
            if (sum < (x ^ *it))
                ans = *it;
            printf("%d\n", ans);
        }
    }
    return 0;
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/riba2534/article/details/80344026
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞