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;
}

 

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