zoukankan      html  css  js  c++  java
  • 可持久化Trie

    ---恢复内容开始---

      HAOI 2019 DAY1 T1 我爆零了。

    爆零的感觉很难受 原因竟然是我从没犯过的错误 审题不清。情绪低迷。

    也许 也许 也许就是想让我知道我有多菜吧。

    求前k大的区间异或值 。我硬生生读错题目 想着将区间分成k段 求划分整个区间的最大值。

    我还写了一个n^3的dp 觉得只能过60 然后搞了一棵trie树 觉得能过80 然后 发现GG了

    这个读错题的我就非常的毒瘤了。 我的RP可能是谷底了吧。

    这个范围 60分随便写啊 n^2暴力 然后 放堆里出来k个即可。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cstdlib>
    #include<cctype>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<algorithm>
    #include<vector>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<map>
    #include<cmath>
    #define ll long long
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline ll read()
    {
        ll x=0,f=1;char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    inline void put(ll x)
    {
        x<0?putchar('-'),x=-x:0;
        ll num=0;char ch[70];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const ll MAXN=500002;
    ll n,k,ans;
    ll a[MAXN],w[MAXN];
    priority_queue<ll> q;
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();k=read();
        for(ll i=1;i<=n;++i)
        {
            a[i]=read();
            w[i]=(a[i]^w[i-1]);
            q.push(w[i]);
        }
        for(ll i=1;i<=n;++i)
            for(ll j=i+1;j<=n;++j)
            {
                ll x=(w[i]^w[j]);
                q.push(x);
            }
        for(ll i=1;i<=k;++i)
        {
            ans+=q.top();
            q.pop();
        }
        put(ans);
        return 0;
    }
    View Code

    考虑正解 首先构建01trie 自然 考虑如何求出第k大 好像不太好求 因为我好像没办法标记 或者说标记某个东西用过了的话会很困难的。

    所以这时 可持久化trie 就出来了 也很自然吧 像主席树一般。

    求第k大这不秒了么,主席树就是来求第k大的 然后 每次根据某个右端点求出左端点即可。

    很简单的题目我却因为 种种非常蠢的原因爆零 甚至连 60都搞不到真是服气我自己 机会不多,自己不珍惜那么 将会永远后悔。

    值得一提的是 这道题在loj上我自己写的大常数代码秒过但是洛谷上就 一直T

    各种优化常数 这里我总结一下T一个点或几个点的优化方法:

    1 不要将所有的 int 都换成long long 这样会很慢的。

    2 加上inline Register 

    3 空间开的不要过大

    4 一些不需要的代码可以优化的要进行优化。

    // luogu-judger-enable-o2
    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cstdlib>
    #include<cctype>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<algorithm>
    #include<vector>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<map>
    #include<cmath>
    #define ll long long
    #define R register
    using namespace std;
    char buf[1<<16],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline ll read()
    {
        ll x=0,f=1;char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    inline void put(ll x)
    {
        x<0?putchar('-'),x=-x:0;
        ll num=0;char ch[70];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const ll MAXN=500010,maxn=35;
    ll n,k,ans,cnt,sum;
    ll w[MAXN];
    int root[MAXN],rank[MAXN];
    int trie[MAXN*maxn][2],sz[MAXN*maxn];
    priority_queue<pair<ll,int> > b;
    inline void insert(int &now,int last,int depth,ll x)
    {
        if(!now)now=++cnt;
        if(depth==0)
        {
            sz[now]++;
            sz[now]+=sz[last];
            return;
        }
        int tn=(x>>(depth-1))&1;
        trie[now][tn^1]=trie[last][tn^1];
        insert(trie[now][tn],trie[last][tn],depth-1,x);
        sz[now]=sz[trie[now][tn]]+sz[trie[now][tn^1]];
        return;
    }
    inline void find(int now,int k,int depth,ll x)
    {
        //if((!trie[now][0])&&(!trie[now][1]))return;
        if(depth==0)return;
        ll tn=(x>>(depth-1))&1;
        if(sz[trie[now][tn^1]]>=k)
        {
            ans=(ans<<1)|1;
            find(trie[now][tn^1],k,depth-1,x);
        }
        else 
        {
            ans=ans<<1;
            find(trie[now][tn],k-sz[trie[now][tn^1]],depth-1,x);
        }
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();k=read();
        for(R int i=1;i<=n;++i)
        {
            ll x=read();
            rank[i]=1;
            w[i]=(x^w[i-1]);
        }
        insert(root[0],root[0],33,0);
        for(R int i=1;i<=n;++i)insert(root[i],root[i-1],33,w[i]);
        for(R int i=1;i<=n;++i)
        {
            ans=0;
            find(root[i],rank[i],33,w[i]);
            ++rank[i];
            b.push(make_pair(ans,i));
            //put(ans);
        }
        for(R int i=1;i<=k;++i)
        {
            int l=b.top().second;
            ll z=b.top().first;
            b.pop();sum+=z;//put(z);
            ans=0;
            find(root[l],rank[l],33,w[l]);
            ++rank[l];
            b.push(make_pair(ans,l));
        }
        put(sum);
        return 0;
    }
    View Code

    要是我能刷到这道题 也不至于会爆零了吧 我会证明我有坚强的毅力刷这个题库的。

    再次看错了题目 两个数之间的异或最大值并非一段区间的最大值。

    这样的话就会比上面的题目就比较简单了。

    /**************************************************************
        Problem: 3689
        User: chdy
        Language: C++
        Result: Accepted
        Time:5056 ms
        Memory:43884 kb
    ****************************************************************/
     
    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cstdlib>
    #include<cctype>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<algorithm>
    #include<vector>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<map>
    #include<cmath>
    #define ll long long
    #define INF -1
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[70];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar(' ');return;
    }
    const int MAXN=100002,maxn=34;
    int n,k,cnt,ans;
    int sz[MAXN*maxn],root[MAXN],rank[MAXN];
    int a[MAXN],trie[MAXN*maxn][2];
    struct wy
    {   
        int x,z;
        friend int operator <(wy a,wy b){return a.z>b.z;}
    };
    priority_queue<wy>q;
    inline void insert(int &now,int last,int depth,int x)
    {
        if(!now)now=++cnt;
        if(depth==0)
        {
            sz[now]++;
            sz[now]+=sz[last];
            return;
        }
        int tn=(x>>(depth-1))&1;
        trie[now][tn^1]=trie[last][tn^1];
        insert(trie[now][tn],trie[last][tn],depth-1,x);
        sz[now]=sz[trie[now][tn]]+sz[trie[now][tn^1]];
        return;
    }
    inline void find(int now,int depth,int k,int x)
    {
        if(depth==0)return;
        if(sz[trie[now][0]]+sz[trie[now][1]]<k){ans=INF;return;}
        int tn=(x>>(depth-1))&1;
        if(sz[trie[now][tn]]>=k)
        {
            ans=ans<<1;
            find(trie[now][tn],depth-1,k,x);
        }
        else
        {
            ans=ans<<1|1;
            find(trie[now][tn^1],depth-1,k-sz[trie[now][tn]],x);
        }
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();k=read();
        for(int i=1;i<=n;++i)a[i]=read(),rank[i]=1;
        for(int i=2;i<=n;++i)insert(root[i],root[i-1],33,a[i-1]);
        for(int i=2;i<=n;++i)
        {
            ans=0;
            find(root[i],33,rank[i],a[i]);
            ++rank[i];
            q.push((wy){i,ans});
        }
        for(int i=1;i<=k;++i)
        {
            int l=q.top().x;
            int xx=q.top().z;
            q.pop();ans=0;
            if(i!=k)put(xx);
            else printf("%d
    ",xx);
            find(root[l],33,rank[l],a[l]);
            ++rank[l];
            if(ans!=-1)q.push((wy){l,ans});
        }
        return 0;
    }
    View Code

    这道题就是典型的 可持久化trie 树的应用了 当然 比较基础。

    因为这些都不带修改 带修改的trie树 可能 就可以树套树了 像带修改的主席树一般。

    针对这道题 求 A[p]~A[N]^x之间的最大异或和 考虑 取前缀和那么问题转换成了 w[N]^x^w[p-1];

    至于 p的范围 l~r之间 所以p-1 就是l-1~r-1之间了 取决策时 只需在r-1这个trie树上取。

    至于l-1 让r-1 - l-2的值 然后判断是否可走即可。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cstdlib>
    #include<cctype>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<algorithm>
    #include<vector>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<map>
    #include<cmath>
    #define ll long long
    #define R register
    using namespace std;
    char buf[1<<16],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[70];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int MAXN=600002,maxn=26;
    int n,m,cnt,ans;
    int trie[MAXN*maxn][2],sz[MAXN*maxn];
    int w[MAXN],root[MAXN];
    char ch[2];
    inline void insert(int &now,R int last,R int depth,R int x)
    {
        if(!now)now=++cnt;
        if(!depth)
        {
            ++sz[now];
            sz[now]+=sz[last];
            return;
        }
        R int tn=x>>(depth-1)&1;
        trie[now][tn^1]=trie[last][tn^1];
        insert(trie[now][tn],trie[last][tn],depth-1,x);
        sz[now]=sz[trie[now][tn]]+sz[trie[now][tn^1]];
    }
    inline void find(R int now,R int last,R int depth,R int x)
    {
        if(!depth)return;
        R int tn=x>>(depth-1)&1;
        if(last==-1)
        {
            if(sz[trie[now][tn^1]]>0)
            {
                ans=ans<<1|1;
                find(trie[now][tn^1],last,depth-1,x);
            }
            else
            {
                ans=ans<<1;
                find(trie[now][tn],last,depth-1,x);
            }
            return;
        }
        if(sz[trie[now][tn^1]]-sz[trie[last][tn^1]]>0)
        {
            ans=ans<<1|1;
            find(trie[now][tn^1],trie[last][tn^1],depth-1,x);
        }
        else
        {
            ans=ans<<1;
            find(trie[now][tn],trie[last][tn],depth-1,x);
        }
        return;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        for(R int i=1;i<=n;++i)w[i]=read()^w[i-1];
        insert(root[0],root[0],25,0);
        for(R int i=1;i<=n;++i)insert(root[i],root[i-1],25,w[i]);
        for(R int i=1;i<=m;++i)
        {
            R int x,l,r;
            scanf("%s",ch+1);
            if(ch[1]=='A')
            {
                x=read();++n;
                w[n]=x^w[n-1];
                insert(root[n],root[n-1],25,w[n]);
            }
            else
            {
                l=read();r=read();x=read();
                ans=0;
                find(root[r-1],(l-2>=0)?root[l-2]:-1,25,x^w[n]);
                put(ans);
            }
        }
        return 0;
    }
    View Code

    注意边界 减到-1的处理。。。

    当然 书上还有较为简洁的代码,但是我自认为自己代码的常数小所以就不抄书上的代码了。

    还是自己写的好!(其实大致思路都是一样的)

    可持久化trie 就到这里了 其实真的跟主席树差不多。

  • 相关阅读:
    Javascript 运动中Offset的bug——逐行分析代码,让你轻松了解运动的原理
    Javascript 多物体运动——逐行分析代码,让你轻松了解运动的原理
    thymeleaf设置网页脚本里面的值
    win10系统瞬间黑屏问题
    Spring boot启动端口设置
    idea打包jar包
    java socket使用例子
    java使用socket读取网页
    java读取网页
    Error:Unexpected lock protocol found in lock file. Expected 3, found 0
  • 原文地址:https://www.cnblogs.com/chdy/p/10673469.html
Copyright © 2011-2022 走看看