可持久化專題
一切之始主席樹https://blog.csdn.net/weixin_44879687/article/details/98186932
可持久化並查集
https://www.luogu.com.cn/problem/P3402
https://www.luogu.com.cn/blog/saisbook/solution-p3402
https://www.luogu.com.cn/blog/flashblog/solution-p3402
//首先可持久化並查集我們是用主席樹來維護的,葉子節點維護每個位置的fa
//維護2個量:每個點的父親和每棵樹的根節點的高度。
//那麼不使用路徑壓縮的話這個版本的主席樹和上個版本的主席樹
//有且只有一個點會被改動。
//那麼就可以進行可持久化了。
//既然取消了路徑壓縮,為了保證複雜度,我們需要進行按秩合併。
//按秩合併其實就是,我們以樹高為秩,每次將樹高低的樹合併到樹高高的樹上,
//那麼對於樹高高的樹來說樹高並沒有變(也就說原樹訪問根節點的代價並沒有變),而樹高低的樹訪問根節點的代價只增加了\text{1}1(這棵樹的根節點變成了樹高高的樹的兒子)。
//還有一種特殊情況那就是兩棵樹樹高相同。
//這個時候把樹A合併到樹B上B的樹高會增加1,而
//樹A的高度則不會改變。
//rta,rtb其實是a點和b點在主席樹裡的編號,depth指的是樹高
#include <bits/stdc++.h>
const int N = 1e5 + 10;
int n, m, i, j, k, cntu;
int fa[N << 7], depth[N << 7], root[N << 1], lc[N << 7], rc[N << 7];
int build(int l, int r) {//葉子節點維護每個位置的fa
int u = ++cntu;
if (l == r) { fa[u] = l; return u; }
int mid = (l + r) >> 1;
lc[u] = build(l, mid);
rc[u] = build(mid + 1, r);
return u;
}
int query(int u, int l, int r, int x) {//尋找點x的父親,找到x的葉子結點
if (l == r) return u;
int mid = (l + r) >> 1;
if (x <= mid) return query(lc[u], l, mid, x);
else return query(rc[u], mid + 1, r, x);
}
int update(int pre, int x, int y, int l, int r) {// u的父親變了,用持久化方法構造
int u = ++cntu; lc[u] = lc[pre], rc[u] = rc[pre];
if (l == r) {
fa[u] = y;
depth[u] = depth[pre];//矮樹併到高樹,高度等於他
return u;
}
int mid = (l + r) >> 1;
if (x <= mid) lc[u] = update(lc[pre], x, y, l, mid);
else rc[u] = update(rc[pre], x, y, mid + 1, r);
return u;
}
void add(int u, int l, int r, int x) {//在兩棵樹高度相等情況下,有棵樹要+1
if (l == r) { depth[u]++; return; }
int mid = (l + r) >> 1;
if (x <= mid) add(lc[u], l, mid, x);
else add(rc[u], mid + 1, r, x);
}
int find(int v, int x) {//找到x在版本v的父親
int y = query(root[v], 1, n, x);
if (fa[y] == x) return y;
return find(v, fa[y]);
}
int main() {
scanf("%d %d", &n, &m);
root[0] = build(1, n);
for (int i = 1, op, a, b, v; i <= m; i++) {
scanf("%d", &op), root[i] = root[i - 1];
if (op == 1) {
scanf("%d %d", &a, &b);
int rta = find(i, a), rtb = find(i, b);
if (depth[rta] > depth[rtb]) std::swap(rta, rtb);//高度小的併到大的上
if (fa[rta] == fa[rtb]) continue;
root[i] = update(root[i - 1], fa[rta], fa[rtb], 1, n);//版本更新
if (depth[rta] == depth[rtb]) add(root[i], 1, n, fa[rtb]);
} else if (op == 2) scanf("%d", &v), root[i] = root[v];
else {
scanf("%d %d", &a, &b);
int rta = find(i, a), rtb = find(i, b);
puts(fa[rta] == fa[rtb] ? "1" : "0");
}
}
return 0;
}
可持久化字典樹 區間
https://www.luogu.com.cn/blog/sdlang/Trie-study-text
https://www.cnblogs.com/Judge/p/9498797.html
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <math.h>
#define ll long long
using namespace std;
int cnt[1000010*31],c[1000010*31][2],tot,root[1000010];
int clone()
{
tot++;
c[tot][0]=c[tot][1]=0;
return tot;
}
void insert(int last,int &now,int x)//類似主席樹的思想
{
int tmp=now=clone();
for(int i=27;i>=0;i--)
{
int k=(x>>i)&1;
c[tmp][k]=clone(),c[tmp][k^1]=c[last][k^1];//沒有的連上和他字首一樣的別人
tmp=c[tmp][k],last=c[last][k];//有的自己新建一個,
cnt[tmp]=cnt[last]+1;//字首個數+1
}
//cnt[tmp]=cnt[last]+1;
}
int query(int l,int r,int x)
{
int ans=0;
for(int i=27;i>=0;i--)
{
int k=(x>>i)&1;
//printf("%d %d %d %d\n",cnt[c[r][k^1]],cnt[c[l][k^1]],c[r][k^1],c[l][k^1]);
if(cnt[c[r][k^1]]>cnt[c[l][k^1]])//字首個數相減
{
ans+=(1<<i),r=c[r][k^1],l=c[l][k^1];//注意ans是+號。
}
else
{
r=c[r][k],l=c[l][k];
}
}
return ans;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
insert(0,root[0],0);
int x,tmp=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
tmp^=x;
insert(root[i-1],root[i],tmp);
}
while(q--)
{
char op ;cin>>op;
if(op=='A')
{
n++;
scanf("%d",&x);
tmp^=x;
insert(root[n-1],root[n],tmp);
}
else
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
l--,r--;
if (l == r && l == 0)
printf("%d\n", tmp^ x);
else printf("%d\n",query(root[max(l-1,0)],root[r],tmp^x));
}
}
}
樹上可持久化字典樹
//其實樹上 可持久化 trie 和樹上主席樹類似,
//就是當前節點呼叫的 las 節點變成了該節點的父節點,
//查詢的時候也是和樹上主席樹類似的套路,
#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#define clr(a,b) memset(a,b,sizeof(a))
#define lson lr<<1,l,mid
#define rson lr<<1|1,mid+1,r
#define key_value ch[ch[root][1]][0]
#pragma comment(linker, "/STACK:102400000000,102400000000")
typedef long long LL;
const LL MOD = 1000000007;
const int N = 1e5+15;
const int maxn = 1e6+15;
const int letter = 130;
const int INF = 1e9;
const double pi=acos(-1.0);
const double eps=1e-10;
using namespace std;
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;
}
int n,q,head[N],tot,a[N];
int fa[N][18],deep[N],bin[18];
int ch[N*18][2],sum[N*18],root[N],cnt;
struct edges{
int to,next;
}e[N<<1];
void addedges(int u,int v){
e[tot].to=v,e[tot].next=head[u],head[u]=tot++;
}
int trie_insert(int x,int val){
int tmp,y;
tmp=y=++cnt;
for(int i=16;i>=0;i--){
ch[y][0]=ch[x][0],ch[y][1]=ch[x][1];
sum[y]=sum[x]+1;//相等的字首個數
int t=val&bin[i];
t>>=i;
x=ch[x][t];
ch[y][t]=++cnt;
y=ch[y][t];
}
sum[y]=sum[x]+1;
return tmp;
}
int trie_query(int x,int y,int z,int val){
int ans=0,ps=a[z];
x=root[x],y=root[y],z=root[z];
for(int i=16;i>=0;i--){
int p=(val>>i)&1;//字首個數滿足條件
if(sum[ch[x][p^1]]+sum[ch[y][p^1]]-2*sum[ch[z][p^1]]>0){
x=ch[x][p^1],y=ch[y][p^1],z=ch[z][p^1];
ans|=(1<<i);
}
else x=ch[x][p],y=ch[y][p],z=ch[z][p];
}
return max(ans,val^ps);
}
void dfs(int x,int g){
root[x]=trie_insert(root[g],a[x]);
for(int i=head[x];i!=-1;i=e[i].next){
int to=e[i].to;
if(to==g) continue;
fa[to][0]=x;
deep[to]=deep[x]+1;
dfs(to,x);
}
}
int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
int t=deep[x]-deep[y];
for(int i=16;i>=0;i--) if(t&bin[i])x=fa[x][i];
for(int i=16;i>=0;i--)
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
if(x==y) return x;
return fa[x][0];
}
void init(){
tot=cnt=0;
clr(head,-1);
deep[1]=0;
clr(sum,0),clr(ch,0),clr(root,0),clr(fa,0);
}
int main(){
int x,y,z,v;
bin[0]=1;
for(int i=1;i<=16;i++) bin[i]=bin[i-1]<<1;
while(~scanf("%d%d",&n,&q)){
init();
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
addedges(x,y),addedges(y,x);
}
fa[1][0]=0;
dfs(1,0);
for(int j=1;j<=16;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
while(q--){
scanf("%d%d%d",&x,&y,&v);
z=lca(x,y);
printf("%d\n",trie_query(x,y,z,v));
}
}
return 0;
}
相關文章
- 可持久化trie持久化
- 可持久化 01 trie持久化
- 可持久化線段樹持久化
- 可持久化資料結構持久化資料結構
- 物件持久化問題物件持久化
- 可持久化資料結構1持久化資料結構
- bzoj 4546: codechef XRQRS [可持久化Trie]持久化
- BZOJ3673 : 可持久化並查集持久化並查集
- RDD持久化,不使用RDD持久化的問題的工作原理持久化
- 「學習筆記」可持久化線段樹筆記持久化
- Redis資料持久化—RDB持久化與AOF持久化Redis持久化
- 關於pinia持久化問題失敗這件事(另附官方持久化教程)持久化
- 【資料結構】可持久化線段樹初步資料結構持久化
- P3834 【模板】可持久化線段樹 2持久化
- 區間k小值(可持久化線段樹)持久化
- 關於物件持久化的問題物件持久化
- Redis的兩種持久化方式-快照持久化(RDB)和AOF持久化Redis持久化
- 洛谷P3835 【模板】可持久化平衡樹持久化
- 可持久化線段————主席樹(洛谷p3834)持久化
- redis系列:RDB持久化與AOF持久化Redis持久化
- 巧用DSRM密碼同步將域控許可權持久化密碼持久化
- 演算法隨筆——主席樹(可持久化線段樹)演算法持久化
- Redis 持久化Redis持久化
- Redis - 持久化Redis持久化
- redisaof持久化Redis持久化
- ehcache持久化持久化
- Docker 持久化Docker持久化
- redis持久化Redis持久化
- [Redis]持久化Redis持久化
- RedisStack部署/持久化/安全/與C#專案整合Redis持久化C#
- 【Immutable.js】可持久化資料結構以及結構分享JS持久化資料結構
- 【主席樹】P3919 【模板】可持久化線段樹 1持久化
- SpringCloud使用Sentinel,Sentinel持久化,Sentinel使用nacos持久化SpringGCCloud持久化
- redis持久化開發運維常見問題Redis持久化運維
- .Net Core—-關於MVC中TempData持久化問題MVC持久化
- 擴充套件的持久化上下文問題套件持久化
- Zustand:狀態持久化在專案中的應用持久化
- redis ——AOF持久化Redis持久化