可持久化0-1Trie樹

我跟可持久化數據結構槓上了 \(QwQ\) 。三天模擬賽考了兩次可持久化數據結構(主席樹、可持久化0-1Trie樹),wocc++


目錄:

  • 我的理解
  • 時空複雜度分析
  • 例題及簡析

1、我的理解

可持久化0-1Trie樹,是一種能夠快速查詢區間異或信息的高級數據結構。算法

它的主要思想和主席樹相同,即保存每次插入操做的歷史版本,來快速查詢區間的異或信息。數據結構

0-1Trie樹和日常寫的strTrie樹相同,都是維護前綴信息的數據結構。不一樣點只有一個,就是0-1Trie樹是維護一個0-1串。可持久化0-1Trie樹運用了貪心的思想,即將序列裏的 \(X\) 按二進制爲拆分,若當前 \(X_i\) (指 \(X\) 二進制拆分後的第 \(i\) 位)是1,咱們就往0-1Trie樹的0邊走;反之就往0-1Trie樹的1邊走。ui

可持久化0-1Trie樹與主席樹相同,也須要動態開點。spa

注意:維護區間異或信息的不止可持久化0-1Trie樹一種,還有線性基等。code


2、時空複雜度分析:

  1. 時間複雜度:ip

    與普通0-1Trie樹相同:\(O(n\log n)\)get

    :strTrie樹的時間複雜度是 \(O(n)\) ,是一種典型的以時間換空間的算法。it

  2. 空間複雜度:
    與普通的0-1Trie樹相同:\(O(\min\{n\log |f(a_i)|,|f(a_i)|\})\) ( \(|f(a_i)|\) 爲值域)。注意常數爲 \(2^5\)1<<5)。io


3、例題及簡析

  1. P4735 最大異或和

    Description:

    給定數列 \(\{a_n\}\) ,支持兩種操做:

    • 在數列尾添加一個數 \(x\) ,數列長度變成 \(n+1\) ;

    • 給定閉區間 \([l,r]\) 和一個數 \(x\) ,求:
      \[ \max_{i=l}^{r}\left \{\left(\bigoplus_{j=i}^{n}a_j \right)\bigoplus x\right \} \]

    Method:

    定義 \(Xorsum_i\)\(\bigoplus_{i=1}^{n}a_i\) ,即前綴異或和。咱們顯然能夠獲得
    \[ \left(\bigoplus_{i=pos}^{n}a_i\right)\bigoplus x=Xorsum_{pos-1}\bigoplus Xorsum_n \bigoplus x \]

    \(x\bigoplus x=0\) , \(x \bigoplus 0=x\)

    咱們發現 \(Xorsum_n\bigoplus x\) 是一個定值,咱們只須要維護 \(Xorsum_{pos-1}\) 便可。

    考慮用可持久化0-1Trie樹維護。與主席樹思路相同 ,咱們創建 \(n+1\) 個版本的0-1Trie樹,查詢的時候運用貪心的思路便可。

    可持久化線段樹一樣支持「前綴和」的思想,咱們最後只須要在第 \(r\) 個版本的0-1Trie樹上查找 \(l\) 位置便可。

    本題毒瘤卡常,本人人醜常數大,用了fread等各類卡常操做才經過。而且因爲luogu評測姬的緣由(大霧,已經經過的代碼又會T掉woc。卡不過的話,開o2吧。

    Code:

    #include<bits/stdc++.h>
    #define Maxn 600010
    #define Maxdep 23
    #define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    char buf[1<<21],*p1=buf,*p2=buf;
    inline void read(int &x)
    {
        int f=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        x*=f;
    }
    int n,m;
    int sum[Maxn];
    struct trie
    {
        trie *chd[2];
        int symbl;
        trie()
        {
            for(int i=0;i<2;i++) chd[i]=NULL;
            symbl=0;
        }
    }*root[Maxn],tree[Maxn<<5],*tail;
    void Init(){tail=tree;} 
    void build(trie *&p,int dep)
    {
        p=new (tail++)trie();
        if(dep<0) return ;
        build(p->chd[0],dep-1);
    }
    void update(trie *&p,trie *flag,int dep,int i)
    {
        p=new (tail++)trie();
        if(flag) *p=*flag;
        if(dep<0) return (void)(p->symbl=i);
        int tmp=(sum[i]>>dep)&1;//判斷是1仍是0 
        if(!tmp) update(p->chd[0],flag?flag->chd[0]:NULL,dep-1,i);
        else update(p->chd[1],flag?flag->chd[1]:NULL,dep-1,i);
        if(p->chd[0]) p->symbl=std::max(p->symbl,p->chd[0]->symbl);
        if(p->chd[1]) p->symbl=std::max(p->symbl,p->chd[1]->symbl);
    }
    int query(trie *p,int x,int dep,int limit)
    {
        if(dep<0) return sum[p->symbl]^x;
        int tmp=(x>>dep)&1;
        if(p->chd[tmp^1]&&p->chd[tmp^1]->symbl>=limit) return query(p->chd[tmp^1],x,dep-1,limit);
        return query(p->chd[tmp],x,dep-1,limit);
    }
    signed main()
    {
        Init();
        read(n),read(m);
        build(root[0],Maxdep);
        for(int i=1,x;i<=n;i++)
        {
            read(x);
            sum[i]=sum[i-1]^x;
            update(root[i],root[i-1],Maxdep,i);
        } 
        for(int i=1;i<=m;i++)
        {
            char ch=getchar();
            while(ch!='A'&&ch!='Q') ch=getchar();
            if(ch=='A')
            {
                int x;
                read(x);
                n++;
                sum[n]=sum[n-1]^x;
                update(root[n],root[n-1],Maxdep,n); 
                continue;
            }   
            if(ch=='Q')
            {
                int l,r,x;
                read(l),read(r),read(x);
                int ans=query(root[r-1],sum[n]^x,Maxdep,l-1);
                printf("%d\n",ans);
                continue;
            }
        }
        return 0;
    }
相關文章
相關標籤/搜索