可持久化trie树
数据结构 可持久化trie树
前言
省选D1T1考察的算法。时至今日才总算残喘\(ac\)..
想当初我打了个\(dp\)自以为是正解 (/▽\)
前置知识
你需要的掌握的算法:
可持久化线段树
倘若你没学过,在百度本小破站学习一波即可
可持久化trie树
事实上,trie树本身就有主席树的一丝神韵。主席树通过重复利用之前的节点,达到节省空间的目的。trie树同样也在原来的树的基础上增增添添,空间复杂度同样优秀。
可持久化trie树,个人感觉和可持久化线段树又是有很大的共同点。回想主席树是在增加一个数据之后增添一条链,构成一个类似前缀和的树;可持久化trie树的精神与其完全一致:对于一个新的字符串,在继承原trie树的基础上,新建一个根节点,然后修修补补,传传承承,大功告成( ̄▽ ̄)*
这里不再赘述,把代码记录一下:
建立新根节点:
void create(int k,int num){
root[k+1]=++tot;
int u=root[k],v=root[k+1];
for(int i=31;i>=0;--i){
ch[v][0]=ch[u][0];
ch[v][1]=ch[u][1];
bool tar=(1<<i)#
ch[v][tar]=++tot;
u=ch[u][tar]; v=ch[v][tar];
cnt[v]=cnt[u]+1;
}
}
01trie树
想起trie树,自然会联想到一个毫无相关的运算:按位异或 (\(xor\))
当我们把一个trie树建成01trie树之后,再将数字拆成二进制,像字符串一样插入。这样,我们就能快速完成有关异或的操作。
例如需要求最大异或操作:我们只需从根节点顺序往下遍历,倘若从当前节点有一条记录着与原数上二进制位不同的边,那贪心地思考,必须走着一条。因为就算后面位数与原数二进制位全部相同,其和也不及这条边带来的收益大。
例题
最大异或和
题目简述:
给出一个数列,需要支持一下操作
支持在原数列末尾增添一个数字,使得数列大小\(+1\)
支持在选择给定范围内的一个位置\(p\),并给定\(x\),使得\(a[p]\oplus a[p+1]\oplus...\oplus a[n]\oplus x\)取得最大值
假设\(s[i]\)表示前缀异或和,那么原式就可以表示为 \(s[p-1]\oplus s[n] \oplus x\)最大,而后两项均一致,我们本质上要求的就是区间内异或值最大
联系上静态区间第\(k\)小的做法,直接在两棵01trie树上通过减去前缀获得目标区间的信息,然后求异或的最大值即可
// luogu-judger-enable-o2
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int add;
struct trie{
static const int MAX=4e7+5;
int tot,root[MAX],ch[MAX][2],cnt[MAX];
void init(){
tot=1; root[0]=tot;
tot=2; root[1]=tot;
int u=root[1];
for(int i=31;i>=0;--i){
ch[u][0]=++tot;
u=ch[u][0];
cnt[u]++;
}
}
void create(int k,int num){
root[k+1]=++tot;
int u=root[k],v=root[k+1];
for(int i=31;i>=0;--i){
ch[v][0]=ch[u][0];
ch[v][1]=ch[u][1];
bool tar=(1<<i)#
ch[v][tar]=++tot;
u=ch[u][tar]; v=ch[v][tar];
cnt[v]=cnt[u]+1;
}
}
bool exist(int p,int k){
int u=root[p+1];
for(int i=31;i>=0;--i){
bool tar=(1<<i)&k;
if(!ch[u][tar]) return false;
u=ch[u][tar];
}
return true;
}
int query(int l,int r,int x){
return xormax(l-1,r,x);
}
int xormax(int a,int b,int k){
int u=root[a],v=root[b];
int ans=0;
for(int i=31;i>=0;--i){
bool tar=(1<<i)&k;
if(cnt[ch[u][tar^1]]!=cnt[ch[v][tar^1]]){
ans+=(1<<i);
u=ch[u][tar^1]; v=ch[v][tar^1];
}
else{
u=ch[u][tar]; v=ch[v][tar];
}
}
return ans;
}
}trie;
int n,m;
inline int read();
inline char getc();
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
n=read(); m=read();
trie.init();
for(int i=1;i<=n;++i){
add=add^read();
trie.create(i,add);
}
for(int i=1;i<=m;++i){
char type=getc(); int l,r,x;
switch(type){
case 'A':
x=read(); add=add^x; n++;
trie.create(n,add);
break;
case 'Q':
l=read(); r=read(); x=read();
printf("%d\n",trie.query(l,r,x^add));
break;
}
}
return 0;
}
inline int read(){
char tmp=getchar(); int sum=0; bool flag=false;
while(tmp<'0'||tmp>'9'){
if(tmp=='-') flag=true;
tmp=getchar();
}
while(tmp>='0'&&tmp<='9'){
sum=(sum<<1)+(sum<<3)+tmp-'0';
tmp=getchar();
}
return flag?-sum:sum;
}
inline char getc(){
char tmp=getchar();
while(tmp!='A'&&tmp!='Q') tmp=getchar();
return tmp;
}
P5283 [十二省联考2019]异或粽子
省选的第一题,考察了可持久化trie树。
同样是前缀异或和的思想,问题可以转化为求前\(k\)大的\(s[i]\oplus s[j](1\leq l\leq r\leq n)\)
现在我们一脸懵逼。嗯?最大值?没有左边界,也没有右边界怎么判断?
倘若暴力枚举左右边界,显然t到飞起,那么。。
只专注于左边界(右边界同样道理也可行),设为\(l\),那么右边界\(r\)必定出现于\([l+1,n]\)。这一点我们可以用可持久化trie树快速处理出静态区间第k小。
先为每一个左边界处理出其最大的右边界,将答案塞入优先队列中。
当弹出一个答案时,说明该左边界对应的最大右边界已经作为答案贡献过了,那么接下来最可能为答案做贡献的便是第二大的右边界。再次利用到可持久化trie树快速解决
这样,不断弹出答案的同时更改答案,重复\(k\)次即为答案。
双倍经验~~ [NOI2010]超级钢琴
这道题和省选题极其相似。不同的是省选题多了个trie树作为难点,本题则是在区间上作出了略微限制。略微思考,略微思考,略微修改,即可略微\(wa\)= ̄ω ̄=
省选题的代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int MAX=5e5+5;
int n,k,l,r,ans;
int a[MAX],s[MAX];
int tot,root[MAX];
struct datan{
int num,id;
}num[MAX];
int rk[MAX],sa[MAX];
struct tree{
int l,r,val;
}node[40000005];
struct data{
int u,limx,limy,lim,k,delta;
bool operator < (const data &a) const{
return delta<a.delta;
}
};
priority_queue <data> line;
inline int read();
bool cmpn(datan a,datan b) {return a.num<b.num;}
bool cmpi(datan a,datan b) {return a.id<b.id;}
void build(int,int,int);
void updata(int,int,int,int,int);
int qsum(int,int,int,int,int);
int kth(int,int,int);
int query(int,int,int,int,int);
main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
cin>>n>>k>>l>>r;
for(register int i=1;i<=n;++i) a[i]=read(),s[i]=s[i-1]+a[i];
for(register int i=1;i<=n;++i) num[i].num=s[i],num[i].id=i;
sort(num+1,num+1+n,cmpn);
for(register int i=1;i<=n;++i) sa[i]=num[i].id;
for(register int i=1;i<=n;++i) rk[sa[i]]=i;
root[0]=++tot; build(root[0],1,MAX);
for(register int i=1;i<=n;++i) root[i]=++tot,updata(root[i-1],root[i],1,MAX,rk[i]);
for(register int i=1;i<=n-l+1;++i){
data tmp;
tmp.u=i; tmp.limx=i+l-1; tmp.limy=min(i+r-1,n);
tmp.lim=tmp.limy-tmp.limx+1; tmp.k=1;
tmp.delta=kth(tmp.limx,tmp.limy,1)-s[tmp.u-1];
line.push(tmp);
}
while(k--){
data tmp=line.top(); line.pop();
ans+=tmp.delta;
if(tmp.k==tmp.lim) continue;
tmp.k++; tmp.delta=kth(tmp.limx,tmp.limy,tmp.k)-s[tmp.u-1];
line.push(tmp);
}
cout<<ans<<endl;
return 0;
}
inline int read(){
char tmp=getchar(); int sum=0; bool flag=false;
while(tmp<'0'||tmp>'9'){
if(tmp=='-') flag=true;
tmp=getchar();
}
while(tmp>='0'&&tmp<='9'){
sum=(sum<<1)+(sum<<3)+tmp-'0';
tmp=getchar();
}
return flag?-sum:sum;
}
void build(int p,int l,int r){
if(l==r) return;
node[p].l=++tot; node[p].r=++tot;
int mid=(l+r)>>1;
build(node[p].l,l,mid); build(node[p].r,mid+1,r);
}
void updata(int pre,int cur,int l,int r,int k){
node[cur].l=node[pre].l;
node[cur].r=node[pre].r;
node[cur].val=node[pre].val+1;
if(l==r) return;
int mid=(l+r)>>1;
if(k<=mid) node[cur].l=++tot,updata(node[pre].l,node[cur].l,l,mid,k);
if(mid+1<=k) node[cur].r=++tot,updata(node[pre].r,node[cur].r,mid+1,r,k);
}
int qsum(int p,int l,int r,int L,int R){
if(L<=l&&r<=R) return node[p].val;
int sum=0,mid=(l+r)>>1;
if(L<=mid) sum+=qsum(node[p].l,l,mid,L,R);
if(mid+1<=R) sum+=qsum(node[p].r,mid+1,r,L,R);
return sum;
}
int kth(int a,int b,int c){
return query(root[a-1],root[b],1,MAX,b-a-c+2);
}
int query(int a,int b,int l,int r,int k){
if(l==r) return s[sa[l]];
int mid=(l+r)>>1;
int x=node[node[b].l].val-node[node[a].l].val;
if(x>=k) return query(node[a].l,node[b].l,l,mid,k);
else return query(node[a].r,node[b].r,mid+1,r,k-x);
}

浙公网安备 33010602011771号