1.可持久化线段树
思想:每次修改时候新开一条链记录修改的值,并且将这条链与原图相连
struct segment_tree{
int id[25000010],ls[25000100],rs[25000010],w[25000010],s[1000010],sz;
int build(int l,int r){//动态开点,记录一个点左右儿子
id[0]=1;
int root=++sz;
if(l==r){
w[root]=s[l];
return root;
}
int mid=(l+r)>>1;
ls[root]=build(l,mid);
rs[root]=build(mid+1,r);
return root;
}
int insert(int l,int r,int lasid,int h,int wz,int x){
int root=++sz;//新加的点
if(l==r){
w[root]=x;
return root;
}
int mid=(l+r)>>1;
if(wz<=mid){//添加的是左节点
ls[root]=insert(l,mid,ls[lasid],h,wz,x);//左节点新加
rs[root]=rs[lasid];//右节点与上次的右节点连接
}else{//同上
ls[root]=ls[lasid];
rs[root]=insert(mid+1,r,rs[lasid],h,wz,x);
}
return id[h]=root;
}
int query(int l,int r,int lasid,int wz,int h){//查找第k次数
int root=++sz;
if(l==r){
w[root]=w[lasid];
return w[lasid];
}
ls[root]=ls[lasid];
rs[root]=rs[lasid];
int mid=(l+r)>>1;
if(wz<=mid){
return query(l,mid,ls[lasid],wz,h);
}else{
return query(mid+1,r,rs[lasid],wz,h);
}
}
};
2.可持久化trie
1) 01trie:将一个数用二进制表示后当作一个字符串插入trie,可以处理异或等操作
每次插入边的权值,然后查询最大值
这里查询运用到了贪心,一个数串从高位到低位依次查询,每次尽量取相反的值(每次去相反可以使最终答案的二进制此位为1)
struct trie{
int dic[500010][2],num;
void insert(int x){
int k=0;
for(int i=30;i>=0;i--){
int ty=((x>>i)&1);
if(!dic[k][ty]) dic[k][ty]=++num;
k=dic[k][ty];
}
}
int get(int x){
int k=0,ans=0;
for(int i=30;i>=0;i--){
int ty=((x>>i)&1);
if(dic[k][ty^1]){
ans|=(1<<i);
k=dic[k][ty^1];
}else{
k=dic[k][ty];
}
}
return ans;
}
};
2)可持久化trie
例子:插入&查询(l~r)~n的异或最大值
首先将异或前缀和求出来,每次求出x^s[n]^s[k](l<=k<=r)
struct trie{
int dic[15000001][2],rt[15000010],latest[15000010],num;
void insert(int x,int id){
int p1=rt[id]=++num;
int p2=rt[id-1];
for(int i=28;i>=0;i--){
latest[p1]=latest[p2]+1;
int ty=(x>>i)&1;
dic[p1][!ty]=dic[p2][!ty];
if(!dic[p1][ty]) dic[p1][ty]=++num;
p1=dic[p1][ty];
p2=dic[p2][ty];
}
latest[p1]=latest[p2]+1;
}
int query(int l,int r,int x){
int ans=0;
int p1=rt[r];
int p2=rt[max(l-1,0)];
for(int i=28;i>=0;i--){
int ty=(x>>i)&1;
if(latest[dic[p1][!ty]]>latest[dic[p2][!ty]]){
ans+=(1<<i);
p1=dic[p1][!ty];
p2=dic[p2][!ty];
}else{
p1=dic[p1][ty];
p2=dic[p2][ty];
}
}
return ans;
}
};