可持久化Trie
---恢复内容开始---
HAOI 2019 DAY1 T1 我爆零了。
爆零的感觉很难受 原因竟然是我从没犯过的错误 审题不清。情绪低迷。
也许 也许 也许就是想让我知道我有多菜吧。
求前k大的区间异或值 。我硬生生读错题目 想着将区间分成k段 求划分整个区间的最大值。
我还写了一个n^3的dp 觉得只能过60 然后搞了一棵trie树 觉得能过80 然后 发现GG了
这个读错题的我就非常的毒瘤了。 我的RP可能是谷底了吧。
这个范围 60分随便写啊 n^2暴力 然后 放堆里出来k个即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#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; }
考虑正解 首先构建01trie 自然 考虑如何求出第k大 好像不太好求 因为我好像没办法标记 或者说标记某个东西用过了的话会很困难的。
所以这时 可持久化trie 就出来了 也很自然吧 像主席树一般。
求第k大这不秒了么,主席树就是来求第k大的 然后 每次根据某个右端点求出左端点即可。
很简单的题目我却因为 种种非常蠢的原因爆零 甚至连 60都搞不到真是服气我自己 机会不多,自己不珍惜那么 将会永远后悔。
值得一提的是 这道题在loj上我自己写的大常数代码秒过但是洛谷上就 一直T
各种优化常数 这里我总结一下T一个点或几个点的优化方法:
1 不要将所有的 int 都换成long long 这样会很慢的。
2 加上inline Register
3 空间开的不要过大
4 一些不需要的代码可以优化的要进行优化。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 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; }
要是我能刷到这道题 也不至于会爆零了吧 我会证明我有坚强的毅力刷这个题库的。
再次看错了题目 两个数之间的异或最大值并非一段区间的最大值。
这样的话就会比上面的题目就比较简单了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/************************************************************** 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; }
这道题就是典型的 可持久化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的值 然后判断是否可走即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#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; }
注意边界 减到-1的处理。。。
当然 书上还有较为简洁的代码,但是我自认为自己代码的常数小所以就不抄书上的代码了。
还是自己写的好!(其实大致思路都是一样的)
可持久化trie 就到这里了 其实真的跟主席树差不多。