可持久化

基操

可以回溯修改前的东西

压缩空间

每次新开节点记录信息,代码极简

模板0

过于简单没有代码

可持久化栈

每次新开节点,回溯时还原直接回到之前的节点

模板1

求区间第k小

将权值线段树yy成数组,这无疑是前缀和相减后二分,所以可持久化

#include<bits/stdc++.h>
using namespace std;

const int N=1e7+5;
int n,m,cnt,c[N],ls[N],rs[N],rt[N];

void bld(int &p,int l,int r,int x) {
	++cnt; c[cnt]=c[p]+1,ls[cnt]=ls[p],rs[cnt]=rs[p];
	p=cnt; 
	if(l==r) return;
	int mid=l+(r-l>>1);
	if(x<=mid) bld(ls[p],l,mid,x);
		else bld(rs[p],mid+1,r,x);
}

int ask(int p1,int p2,int l,int r,int x) {
	if(l==r) {
		return l;
	}
	int mid=l+(r-l>>1),d=c[ls[p1]]-c[ls[p2]];
	if(d>=x) return ask(ls[p1],ls[p2],l,mid,x);
	return ask(rs[p1],rs[p2],mid+1,r,x-d);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) {
		int t; scanf("%d",&t); t+=1e9;
		rt[i]=rt[i-1],bld(rt[i],0,2e9,t);
	}
	for(int i=1;i<=m;i++) {
		int l,r,k; scanf("%d%d%d",&l,&r,&k);
		 printf("%d\n",ask(rt[r],rt[l-1],0,2e9,k)-(int)1e9);
	}
	return 0;
}

模板2

Luogu P3835 【模板】可持久化平衡树

见fhq-treap

模板3

可持久化数组

和区间第K大一毛一样

模板4

Luogu P3402 可持久化并查集

不能路径压缩,必须按秩合并,一个\(lg\)

每次只会改一个父亲,顶多再改一个深度

所以可以用可持久化线段树维护,再一个\(lg\)

注意返回的最好是节点编号(量大好使)

#include<bits/stdc++.h>
using namespace std;

const int M=2e5+5,N=7e6+5;
int n,m,rt[M],ls[N],rs[N],cnt;
struct A {int fa,dep; }c[N];
void bld(int &p,int l,int r) {
	p=++cnt;
	if(l==r) {
		c[p]=(A){l,1};	return;
	}
	int mid=l+r>>1;
	bld(ls[p],l,mid),bld(rs[p],mid+1,r);
}

void upd(int &p,int l,int r,int x,A k) {
	ls[++cnt]=ls[p],rs[cnt]=rs[p];
	p=cnt;
	if(l==r) {
		c[p]=k; return;
	}
	int mid=l+r>>1;
	if(x<=mid) upd(ls[p],l,mid,x,k);
		else upd(rs[p],mid+1,r,x,k);
}

int ask(int p,int l,int r,int x) {
	if(l==r) return p;
	int mid=l+r>>1;
	if(x<=mid) return ask(ls[p],l,mid,x);
	return ask(rs[p],mid+1,r,x); 
}
int find(int p,int x) {
	int t=ask(p,1,n,x);
	for(;c[t].fa!=x;x=c[t].fa,t=ask(p,1,n,x));
	return t;
}
int main(){
	scanf("%d%d",&n,&m);
	bld(rt[0],1,n);
	for(int i=1;i<=m;i++) {
		int op; scanf("%d",&op);
		if(op==1) {
			int u,v; scanf("%d%d",&u,&v);
			int x=find(rt[i-1],u),y=find(rt[i-1],v);
			rt[i]=rt[i-1];
			if(c[x].dep>c[y].dep) {
				upd(rt[i],1,n,c[y].fa,c[x]);
			} else if(c[x].dep==c[y].dep) {
				upd(rt[i],1,n,c[y].fa,c[x]);
				upd(rt[i],1,n,c[x].fa,(A){c[x].fa,c[x].dep+1});
			} else upd(rt[i],1,n,c[x].fa,c[y]);
		} else if(op==2) {
			int k; scanf("%d",&k); 
			rt[i]=rt[k];
		} else {
			int u,v; scanf("%d%d",&u,&v);
			rt[i]=rt[i-1];
			int x=find(rt[i],u),y=find(rt[i],v);
			puts(c[x].fa==c[y].fa?"1":"0");
		}
	}
	return 0;
}

例题1

Luogu P2633 Count on a tree

\(K\)小=权值线段树

维护\(u\)到根的所有点权的权值线段树为\(rt(u)\),设\(u\)->\(v\)权值线段树为\(Rt(u,v)\)

\[Rt(u,v)=rt(u)+rt(v)-rt(lca)-rt(fa(lca)) \]

注意是\(rt(lca)\)\(rt(fa(lca))\),\(lca\)需要算1遍

#include<bits/stdc++.h>
using namespace std;

const int N=4e6+5,M=1e5+5;
int n,m,c[N],ls[N],rs[N],cnt;
int rt[M],f[M][21],lg[M],d[M],a[M],b[M],fid[M];
vector<int>V[M];
void add(int &p,int l,int r,int x) {
	c[++cnt]=c[p]+1,ls[cnt]=ls[p],rs[cnt]=rs[p];
	p=cnt;
	if(l==r) return;
	int mid=l+r>>1;
	if(x<=mid) add(ls[p],l,mid,x);
		else add(rs[p],mid+1,r,x);
}
void dfs(int fa,int u) {
	rt[u]=rt[fa],add(rt[u],1,n,a[u]);
	d[u]=!fa?0:d[fa]+1,f[u][0]=fa;
	for(int i=1;i<=lg[d[u]];i++) {
		f[u][i]=f[f[u][i-1]][i-1];
	}
	for(auto v:V[u]) {
		if(v!=fa) dfs(u,v);
	}
}
inline int lca(int u,int v) {
	if(d[u]>d[v]) swap(u,v);
	while(d[u]<d[v]) {
		v=f[v][lg[d[v]-d[u]]];
	}
	if(u==v) return u;
	for(int i=lg[d[u]];i>=0;i--) {
		if(f[u][i]!=f[v][i]) {
			u=f[u][i],v=f[v][i];
		}
	}
	return f[u][0];
}

inline bool cmp(int i,int j) {
	return a[i]<a[j];
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]),b[i]=i;
	}
	sort(b+1,b+n+1,cmp);
	fid[1]=a[b[1]]; a[b[1]]=1; int tot=1;
	for(int i=2;i<=n;i++) {
		if(a[b[i]]>fid[a[b[i-1]]]) {
			++tot,fid[tot]=a[b[i]];
		}
		a[b[i]]=tot;
	}
	for(int i=1;i<n;i++) {
		int u,v; scanf("%d%d",&u,&v);
		V[u].push_back(v),V[v].push_back(u);
	}
	lg[0]=-1;
	for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
	dfs(0,1); int ans=0;
	while(m--) {
		int u,v,k; scanf("%d%d%d",&u,&v,&k); u^=ans;
		int lc=lca(u,v);
		int l=1,r=n,mid,ret,n1=rt[u],n2=rt[v],n3=rt[lc],n4=rt[f[lc][0]];
		while(l<r) {
			mid=l+r>>1;
			ret=c[ls[n1]]+c[ls[n2]]-c[ls[n3]]-c[ls[n4]];
			if(ret>=k) {
				r=mid;
				n1=ls[n1],n2=ls[n2],n3=ls[n3],n4=ls[n4];
			} else {
				l=mid+1,k-=ret;
				n1=rs[n1],n2=rs[n2],n3=rs[n3],n4=rs[n4];
			}
		}
		printf("%d\n",ans=fid[l]);
	}
	return 0;
}

例题2

Luogu P2839 [国家集训队]middle

中位数通常可以二分答案,然后标记$\geq \(它的为1,比它小的为-1,则序列的中位数一定是满足序列和\)\geq$0的最大数(等号因为相同取后者)

所以可以二分答案,标记,统计中间的和\([b,c]\)+左边的最大前缀(包括不选)+右边的最小前缀(包括不选)

我可能知道为什么我数据结构老要重构了,因为开始时没想好全部,导致中间改得不完整

要不以后还是写两遍吧

#include<bits/stdc++.h>
using namespace std;

const int N=2e4+5,M=1e6+5;
int n,m,a[N],b[N],fid[N],rt[N];
int c[M],ls[M],rs[M],lc[M],rc[M],cnt;
inline bool cmp(int i,int j) {
	return a[i]<a[j];
}
inline void up(int p) {
	c[p]=c[ls[p]]+c[rs[p]];
	lc[p]=max(lc[ls[p]],c[ls[p]]+lc[rs[p]]);
	rc[p]=max(rc[rs[p]],c[rs[p]]+rc[ls[p]]);
}
void add(int &p,int l,int r,int x,int k) {
	c[++cnt]=c[p],ls[cnt]=ls[p],rs[cnt]=rs[p];
	p=cnt;
	if(l==r) {
		c[p]+=k; lc[p]=rc[p]=max(0,c[p]);
		return;
	}
	int mid=l+r>>1;
	if(x<=mid) add(ls[p],l,mid,x,k);
		else add(rs[p],mid+1,r,x,k);
	up(p); 
}
int sum(int p,int l,int r,int x,int y) {
	if(!p||x>y) return 0;
	if(l==x&&r==y) return c[p];
	int mid=l+r>>1;
	if(y<=mid) return sum(ls[p],l,mid,x,y);
	if(x>mid) return sum(rs[p],mid+1,r,x,y);
	return sum(ls[p],l,mid,x,mid)+sum(rs[p],mid+1,r,mid+1,y);
}
int pre(int p,int l,int r,int x,int y) {
	if(!p) return 0;
	if(l==x&&r==y) return lc[p];
	int mid=l+r>>1;
	if(y<=mid) return pre(ls[p],l,mid,x,y);
	if(x>mid) return pre(rs[p],mid+1,r,x,y);
	return max(pre(ls[p],l,mid,x,mid),sum(ls[p],l,mid,x,mid)+pre(rs[p],mid+1,r,mid+1,y));
}
int suf(int p,int l,int r,int x,int y) {
	if(!p) return 0;
	if(l==x&&r==y) return rc[p];
	int mid=l+r>>1;
	if(y<=mid) return suf(ls[p],l,mid,x,y);
	if(x>mid) return suf(rs[p],mid+1,r,x,y);
	return max(suf(rs[p],mid+1,r,mid+1,y),sum(rs[p],mid+1,r,mid+1,y)+suf(ls[p],l,mid,x,mid));
}
int q[5];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]); b[i]=i;
	}
	sort(b+1,b+n+1,cmp);
	for(int i=1;i<=n;i++) {
		add(rt[1],1,n,i,1);
	}
	fid[1]=a[b[1]]; a[b[1]]=1,m=1;
	rt[2]=rt[1],add(rt[2],1,n,b[1],-2);
	for(int i=2;i<=n;i++) {
		if(a[b[i]]>fid[a[b[i-1]]]) {
			fid[++m]=a[b[i]],rt[m+1]=rt[m];
		}
		a[b[i]]=m; add(rt[m+1],1,n,b[i],-2);
	}
	int T; scanf("%d",&T); int ans=0;
	while(T--) {
		scanf("%d%d%d%d",&q[0],&q[1],&q[2],&q[3]);
		for(int i=0;i<4;i++) q[i]=(q[i]+ans)%n+1;
		sort(q,q+4);
		int l=1,r=m,mid,ret; 
		while(l<=r) {
			mid=l+r>>1;
			ret=sum(rt[mid],1,n,q[1],q[2])+suf(rt[mid],1,n,q[0],q[1]-1)+pre(rt[mid],1,n,q[2]+1,q[3]);
			if(ret>=0) {
				ans=mid,l=mid+1;
			} else r=mid-1;
		}
		printf("%d\n",fid[ans]);
		ans=fid[ans];
	}
	return 0;
}
posted @ 2021-03-22 11:15  wwwsfff  阅读(27)  评论(0编辑  收藏  举报