可持久化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('\n');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('\n');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\n",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('\n');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 就到这里了 其实真的跟主席树差不多。

posted @ 2019-04-08 21:17  chdy  阅读(725)  评论(0编辑  收藏  举报