Codeforces 979D Kuro and GCD and XOR and SUM 字典树+位运算+思维

第二次做到这种带异或的题要建字典树的,第一次不会,第二次又没想出来,这笔帐先记下了。。。异或这个东西就是,题目让你求的东西,往往跟它每一位密切相关,跟别的关系不大,所以它才总被用来建字典树。。。这个题的第二种操作,有三个条件,一个是整除的条件,对于这个条件,我们用数学的东西一想,就晓得如果不马上需要输出-1的话,那一定是要查一个能整除k的点。但整除跟异或关系又不大,这咋整呢?

然后神奇操作就来了,就建了1e5个字典树,第i棵树上的点就是能被i整除的数。。。好厉害!真难为给题目开这么大的空间了!我粗略一算,假设真给了1e5个数,数都很大,权且算每个数都出现在几十棵树上,然后这个空间。。。啊呀呀!炸了!可怕!不得了!但是,tutorial就是说要这么干,我至今也不明白怎么会有这么厉害的事。

由于菜鸡我字典树建树经验奇少无比,建树的时候就在想,这咋建呢?最朴素的字典树,查询时移到每一个结点的时候,下一步要走哪都是确定的,目的是确定有无跟现成字符串匹配的路径,今天这个字典树,要求的是最大值,只要树上有大小符合要求的数,就一定有答案,那我咋知道我现在走哪条路就能匹配到长度正好的数呢?于是聪明的操作又有了,就是把所有的数都展开成18位的二进制数,从根节点到叶节点,是从高位到低位的关系。这样就很好地解决了问题,因为我们可以给每一个节点记录一个值,记录的就是这棵子树上最小的数是几。这样一来,每到一个节点,先看下一步能异或出1的子节点,如果这棵子树上的最小值符合要求,就直接走这条路,否则答案一定在另一颗子树上。为啥呢?因为我们一上来在父节点的地方就能判断这棵树上有没有大小合适的点。父结点处如果判定这棵树上有合法的答案,那必定就是有,不是左儿子有,就是右儿子有。

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;

struct Node{
    int minn;
    Node *next[2];
};

struct PTrie{
    Node *root;
    PTrie()
    {
        root=newNode();
    }
    void init(){del(root); root=newNode();}
    Node *newNode()
    {
        Node *u=new Node;
        u->next[0]=NULL; u->next[1]=NULL;
        u->minn=1e5+5;
        return u;
    }
    void insert(int v)
    {
        Node *u=root;
        for (int i=18; i>=0; i--)
        {
            u->minn=min(u->minn,v);
            int tmp=v>>i&1;
            if (u->next[tmp]==NULL)
            {
                u->next[tmp]=newNode();
            }
            u=u->next[tmp];
        }
        u->minn=min(u->minn,v);
    }
    int find(int x,int s)
    {
        Node *u=root;
        int ans=0;
        if (u->minn+x>s) return -1;
        for (int i=18; i>=0; i--)
        {
            int tmp=x>>i&1;
            if (u->next[tmp^1]!=NULL && u->next[tmp^1]->minn+x<=s)
            {
                ans+=((tmp^1)<<i);
                u=u->next[tmp^1];
            }else
            {
                ans+=(tmp<<i);
                u=u->next[tmp];
            }
        }
        return ans;
    }
    void del(Node *rt)
    {
        if (rt==NULL) return;
        else
        {
            if (rt->next[0]) del(rt->next[0]);
            if (rt->next[1]) del(rt->next[1]);
        }
        delete rt;
    }
}trie[100005];

int q;
vector<int> vec[100005];

int main()
{
    scanf("%d",&q);
    for (int i=1; i<1e5+5; i++)
    {
        for (int j=i; j<1e5+5; j+=i)
        {
            vec[j].push_back(i);
        }
    }
    while (q--)
    {
        int id; scanf("%d",&id);
        if (id==1)
        {
            int u; scanf("%d",&u);
            for (int i=0; i<vec[u].size(); i++)
            {
                trie[vec[u][i]].insert(u);
            }
        }else
        {
            int x,k,s; scanf("%d%d%d",&x,&k,&s);
            if (x%k) printf("-1\n");
            else printf("%d\n",trie[k].find(x,s));
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章