可持久化数据结构

CF702F T-Shirts

对于每个人剩下的钱建平衡树,那么我们按照优先级对于每个物品 ( c , p ) (c,p) (c,p),将钱 ≥ c \geq c c的人的钱 − = c -=c =c,答案 + + ++ ++,区间打标记即可,注意到用非旋 t r e a p treap treap维护时 c ≤ x < 2 c c\leq x \lt 2c cx<2c的物品无法直接 m e r g e merge merge,拿出来暴力插入即可,可以证明每次 x x x的权值会缩小至少一半,所以暴力插入次数是 O ( n log ⁡ x ) O(n\log x) O(nlogx)的。
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

int n,m;
int lc[maxn],rc[maxn],val[maxn],key[maxn],ad[maxn],ans[maxn],ada[maxn],rt;
void dt(int u){
	if(!u) return;
	if(ad[u]){
		val[u] += ad[u];
		ad[lc[u]] += ad[u];
		ad[rc[u]] += ad[u];
		ad[u] = 0;
	}
	if(ada[u]){
		ans[u] += ada[u];
		ada[lc[u]] += ada[u];
		ada[rc[u]] += ada[u];
		ada[u] = 0;
	}
}
void merge(int &u,int a,int b){
	if(!a || !b) return (void)(u = a+b);
	dt(a),dt(b);
	if(key[a] < key[b]) u = a , merge(rc[u],rc[a],b);
	else u = b , merge(lc[u],a,lc[b]);
}
void split(int u,int &a,int &b,int v){
	if(!u) return (void)(a = b = 0);
	dt(u);
	if(val[u] >= v) b = u , split(lc[u],a,lc[b],v);
	else a = u , split(rc[u],rc[a],b,v); 
}
int C[maxn],q[maxn],c[maxn];
bool cmp(const int &u,const int &v){ return q[u] == q[v] ? C[u] < C[v] : q[u] > q[v]; }

void ins(int &u,int v){
	int a,b;
	split(u,a,b,val[v]);
	merge(u,a,v),merge(u,u,b);
}

void dfs(int &u,int &v){
	if(!u) return;
	dt(u);
	dfs(lc[u],v),dfs(rc[u],v);
	ins(v,u);
	u = 0;
}

void dfs2(int u){
	if(!u) return;
	dt(u);
	dfs2(lc[u]),dfs2(rc[u]);
}

int main(){
	scanf("%d",&n);
	rep(i,1,n) scanf("%d%d",&C[i],&q[i]),c[i]=i;
	sort(c+1,c+1+n,cmp);
	scanf("%d",&m);
	rep(i,1,m){
		scanf("%d",&val[i]);
		key[i] = rand() << 15 | rand();
		ins(rt,i);
	}
	rep(i,1,n){
		int u = c[i];
		int a , b , c;
		split(rt,a,b,C[u]);
		ad[b] -= C[u] , ada[b] ++;
 		split(b,b,c,C[u]);
 		dfs(b,a);
 		merge(rt,a,c);
	}
	dfs2(rt);
	rep(i,1,m) printf("%d%c",ans[i]," \n"[i==m]);
}

LOJ#120. 持久化序列

A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 300005
#define maxp maxn * 100
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

int rt[maxn];
int lc[maxp],rc[maxp],val[maxp],key[maxp],sz[maxp],tot;
void upd(int u){ sz[u] = sz[lc[u]] + sz[rc[u]] + 1; }
int newnode(int u){
	lc[++tot] = lc[u] , rc[tot] = rc[u] ,val[tot] = val[u] , key[tot] = key[u] , sz[tot] = sz[u];
	return tot;
}
void split(int u,int &a,int &b,int rk){
	if(!u) return (void)(a=b=0);
	u = newnode(u);
	if(sz[lc[u]] + 1 <= rk) a = u , split(rc[u],rc[a],b,rk-sz[lc[u]]-1);
	else b = u , split(lc[u],a,lc[b],rk);
	upd(u);
}
void merge(int &u,int a,int b){
	if(!a || !b) return (void)(u=a+b);
	if(key[a] < key[b]) u = newnode(a) , merge(rc[u] , rc[a] , b);
	else u = newnode(b) , merge(lc[u] , a , lc[b]);
	upd(u);
}
int qry(int u,int rk){
	for(;u;){
		if(sz[lc[u]] + 1 == rk) return val[u];
		if(sz[lc[u]] + 1 < rk) rk -= sz[lc[u]] + 1 , u = rc[u];
		else u = lc[u];
	}
	return -1;
}

int main(){
	int n,tim=0;
	read(n);
	rep(i,1,n){
		int op,t,k,x;
		read(op),read(t),read(k);
		if(op == 1){
			read(x);
			int a,b;
			rt[++tim] = rt[t];
			split(rt[tim],a,b,k-1);
			int u = newnode(0);
			val[u] = x , key[u] = rand() << 15 | rand() , sz[u] = 1;
			merge(rt[tim],a,u);
			merge(rt[tim],rt[tim],b);
		}
		if(op == 2){
			int a,b,c;
			rt[++tim] = rt[t];
			split(rt[tim],b,c,k);
			split(b,a,b,k-1);
			merge(rt[tim],a,c);
		}
		if(op == 3)
			printf("%d\n",qry(rt[t],k));
	}
}

LOJ #6203. 可持久化队列

要求在线:操作树上倍增即可。不知道那个O(n)的算法是什么鬼
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define ui unsigned int
using namespace std;

char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(ui &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

#define lim 22
ui T,ty,f[maxn][lim],sz[maxn],x[maxn];

int main(){
	read(T),read(ty);
	ui ans = 0 , op , ver , t;
	rep(i,1,T){
		read(op) , read(ver);
		ver ^= ans * ty;		
		if(x[ver] == -1) memcpy(f[i],f[ver],sizeof f[i]);
		else{
			f[i][0] = ver;
			rep(j,1,lim-1) f[i][j] = f[f[i][j-1]][j-1];
		}
		if(op == 1){
			read(t);t ^= ans * ty;
			x[i] = t;sz[i] = sz[ver] + 1;
		}
		else{
			x[i] = -1;sz[i] = sz[ver] - 1;
			int u = i;
			per(j,lim-1,0) if(sz[ver] >> j & 1)
				u = f[u][j];
			ans = ans * 31 + x[u];
		}
	}
	printf("%u\n",ans);
}

HDU 6087 Rikka with Sequence

可持久化 t r e a p treap treap的复制操作。
神乎其技的回收点操作。(把空间复杂度保持在线性是真的离谱,但是还是要用 60 M B 60MB 60MB

A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 200005
#define maxp maxn * 5
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;

inline int Ran(){ 
	static int G = 1;	
	return G = 3ll * G % 998244353; 
}

char Start;

int n,m;
int lc[maxp],rc[maxp],sz[maxp],val[maxp],tot,del[maxp],usd[maxp],rt,rta;
LL sm[maxp];

void upd(int u){ sm[u] = sm[lc[u]] + sm[rc[u]] + val[u] , sz[u] = sz[lc[u]] + sz[rc[u]] + 1;}

void split(int u,int &a,int &b,int rk){
	if(!u) return (void)(a = b = 0);
	if(sz[lc[u]] + 1 <= rk) a = u , split(rc[u] , rc[a] , b , rk - sz[lc[u]] - 1);
	else b = u , split(lc[u] , a , lc[b] , rk);
	upd(u);
}
void merge(int &u,int a,int b){
	if(!a || !b) return (void)(u = a+b);
	if(Ran() % (sz[a] + sz[b]) < sz[a]) u = a , merge(rc[u],rc[a],b);
	else u = b , merge(lc[u],a,lc[b]);
	upd(u);
}

int newnode(int u){
	int v;
	if(del[0]) v = del[del[0]--];
	else v = ++tot;
	lc[v] = lc[u] , rc[v] = rc[u] , sz[v] = sz[u] , val[v] = val[u] , sm[v] = sm[u];
	usd[++usd[0]] = v;
	return v;
}
void split_r(int u,int &a,int &b,int rk){
	if(!u) return (void)(a = b = 0);
	u = newnode(u);
	if(sz[lc[u]] + 1 <= rk) a = u , split_r(rc[u] , rc[a] , b , rk - sz[lc[u]] - 1);
	else b = u , split_r(lc[u] , a , lc[b] , rk);
	upd(u);
}
void merge_r(int &u,int a,int b){
	if(!a || !b) return (void)(u = a+b);
	if(Ran() % (sz[a] + sz[b]) < sz[a]) u = newnode(a) , merge_r(rc[u],rc[a],b);
	else u = newnode(b) , merge_r(lc[u],a,lc[b]);
	upd(u);
}

int vis[maxp],tim;
void dfs(int u){
	if(!u) return;
	vis[u] = tim;
	dfs(lc[u]) , dfs(rc[u]);
}
void reuse(){
	assert(tot - del[0] >= maxp - maxn);
	++tim;
	dfs(rt),dfs(rta);
	int cnt = 0;
	rep(i,1,usd[0]) 
		if(vis[usd[i]] != tim) 
			del[++del[0]] = usd[i];
		else
			usd[++cnt] = usd[i];
	usd[0] = cnt;
}

void dfs0(int &u,int v){
	if(!v) return (void)(u = 0);
	u = newnode(v);
	dfs0(lc[u] , lc[v]) , dfs0(rc[u] , rc[v]);
}

char End;

int main(){
	scanf("%d%d",&n,&m);
	rep(i,1,n){
		++tot;usd[++usd[0]] = tot;
		scanf("%d",&val[i]);
		upd(i);
		merge(rta,rta,i);
	}
	dfs0(rt,rta);
	for(int op,l,r,k,a,b,c,d,e,f;m--;){
		scanf("%d%d%d",&op,&l,&r);
		if(op == 1){
			split_r(rt,b,c,r);
			split_r(b,a,b,l-1);
			printf("%lld\n",sm[b]);
			merge_r(rt,a,b),merge_r(rt,rt,c);
			if(tot - del[0] >= maxp - maxn)
				reuse();
		}
		if(op == 2){
			scanf("%d",&k);
			split_r(rt,b,c,l-1);
			split_r(c,c,e,r-l+1);
			split_r(b,a,b,l-k-1);
			l -= k;
			for(;k <= r - l + 1;k<<=1)
				merge_r(b,b,b);
			split_r(b,b,d,r-l+1);
			merge_r(rt,a,b);
			merge_r(rt,rt,e);
			if(tot - del[0] >= maxp - maxn)
				reuse();
		}
		if(op == 3){
			split_r(rta,b,c,r);
			split_r(b,a,b,l-1);
			split_r(rt,e,f,r);
			split_r(e,d,e,l-1);
			merge_r(rt,d,b);
			merge_r(rt,rt,f);
			merge_r(rta,a,b);
			merge_r(rta,rta,c);
			if(tot - del[0] >= maxp - maxn)
				reuse();
		}
	}
}

LOJ #3325. 「SNOI2020」区间和

本来以为是个高论,但是感觉有点离谱,脑筋急转弯题

忽略可持久化,这个不是本题的重点。
对于 0 0 0操作,
可以看出如果 x x x的水面高度 ≥ h \geq h h,则不需要操作,
否则我们直接求出 x x x左边和右边第一个高度 ≥ h \geq h h的挡板,
把这两个挡板之间的水面高度赋值为 h h h即可。
线段树二分+区间赋值。
对于 1 1 1操作,
可以求出 x x x的当前水面高度 h h h
直接求出 x x x左边和右边第一个高度 ≥ h \geq h h的挡板,分别为 L , R L,R L,R
则对于 [ x , R ] [x,R] [x,R]的每一个点 y y y y y y的水面高度会变成 [ x , y ] [x,y] [x,y]中挡板高度的最大值。
[ L , x ] [L,x] [L,x]同理。
我们发现这是和前缀最大值有关的修改
该不会要写李超树把?该不会要把单调栈预处理出来写可持久化平衡树吧,不会吧不会吧
稍微思考一下发现这个操作可以简单打懒标记维护。
t a g l = x tagl = x tagl=x表示从左到右,该区间之外的挡板的最大值是 x x x的修改,
那么传下去给左儿子就是 t a g l l c = x tagl_{lc} = x tagllc=x,传给右儿子就是 t a g l r c = max ⁡ ( x , m x l c ) tagl_{rc} = \max(x,mx_{lc}) taglrc=max(x,mxlc)
也就是维护一下左儿子里面挡板的最高值。
因为只需要单点查询所以我们一直下放标记即可。
对于 2 2 2操作,没什么好说的,需要注意挡板是在格子之间的,用维护格子的线段树同时维护挡板高度需要一定的技巧。
对于 3 3 3操作,不实现的话你 1 1 1操作也写不了。

注意可持久化线段树是可以下放标记的,比较优秀的一种写法是每次直接把根变为新节点,递归的时候下放标记前直接把左右儿子变成新节点即可。
时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

A C   C o d e \mathcal AC \ Code AC Code

//Freopen 讨厌写线段树上二分 
#include<bits/stdc++.h>
#define maxn 200005
#define maxp maxn * 80
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define inf 0x3f3f3f3f
using namespace std;

char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

int n,q,h[maxn],rt[maxn];
int mxh[maxp],mxhr[maxp],fz[maxp],lz[maxp],rz[maxp],lc[maxp],val[maxp],rc[maxp],tot;

#define il inline
il void dtf(int u,int v){
	if(u)
 		fz[u] = val[u] = v;
}
il void dtl(int u,int v){
	if(u)
		fz[u] = -1,
		lz[u] = val[u] = v;
}
il void dtr(int u,int v){
	if(u)
		fz[u] = lz[u] = -1,
		rz[u] = val[u] = v;
}
il int newnode(int u){
	fz[++tot] = fz[u] , lz[tot] = lz[u] , rz[tot] = rz[u];
	mxh[tot] = mxh[u] , mxhr[tot] = mxhr[u],
	lc[tot] = lc[u] , rc[tot] = rc[u] , val[tot] = val[u];
	return tot;
}
il void dt(int u){
	lc[u] = newnode(lc[u]);
	rc[u] = newnode(rc[u]);
	if(rz[u] != -1){
		dtr(rc[u],rz[u]) , dtr(lc[u],max(rz[u],mxhr[rc[u]]));
		rz[u] = -1;
	}
	if(lz[u] != -1){
		dtl(lc[u],lz[u]) , dtl(rc[u],max(lz[u],mxh[lc[u]]));
		lz[u] = -1;
	}
	if(fz[u] != -1){
		dtf(lc[u],fz[u]) , dtf(rc[u],fz[u]);
		fz[u] = -1;
	}
}

il void upd(int u){
	mxh[u] = max(mxh[lc[u]] , mxh[rc[u]]);
	mxhr[u] = max(mxhr[lc[u]] , mxhr[rc[u]]);
}

void Build(int &u,int l,int r){
	u = ++tot;
	if(l == r) return (void)(mxh[u] = h[l] , mxhr[u] = h[l-1]);
	int m = l+r>>1;
	Build(lc[u],l,m) , Build(rc[u],m+1,r);
	upd(u);
}

int qryl2(int u,int l,int r,int h){
	if(l == r) return l;
	int m = l+r>>1;
	if(mxh[rc[u]] >= h) return qryl2(rc[u],m+1,r,h);
	return qryl2(lc[u],l,m,h);
}
int qryl(int u,int l,int r,int p,int h){
	if(l > p) return 0;
	if(r <= p) return mxh[u] < h ? 0 : qryl2(u,l,r,h);
	int m = l+r>>1 , t; 
	if(t = qryl(rc[u],m+1,r,p,h)) return t;
	return qryl(lc[u],l,m,p,h); 
}

int qryr2(int u,int l,int r,int h){
	if(l == r) return l;
	int m = l+r>>1;
	if(mxhr[lc[u]] >= h) return qryr2(lc[u],l,m,h);
	return qryr2(rc[u],m+1,r,h);
}
int qryr(int u,int l,int r,int p,int h){
	if(r < p) return n+1;
	if(l >= p) return mxhr[u] < h ? n+1 : qryr2(u,l,r,h);
	int m = l+r>>1 , t;
	if((t = qryr(lc[u],l,m,p,h)) <= n) return t;
	return qryr(rc[u],m+1,r,p,h); 
}

void insf(int &u,int l,int r,int ql,int qr,int h){
	if(l>qr||ql>r) return;
	if(ql<=l&&r<=qr) return (void)(dtf(u,h));
	int m = l+r>>1;dt(u);
	insf(lc[u],l,m,ql,qr,h),insf(rc[u],m+1,r,ql,qr,h); 
}

void insl(int &u,int l,int r,int ql,int qr,int &h){
	if(l>qr||ql>r) return;
	if(ql<=l&&r<=qr) return (void)(dtl(u,h),h=max(h,mxh[u]));
	int m = l+r>>1;dt(u);
	insl(lc[u],l,m,ql,qr,h) , insl(rc[u],m+1,r,ql,qr,h);
}

void insr(int &u,int l,int r,int ql,int qr,int &h){
	if(l>qr||ql>r) return;
	if(ql<=l&&r<=qr) return (void)(dtr(u,h),h=max(h,mxhr[u]));
	int m = l+r>>1;dt(u);
	insr(rc[u],m+1,r,ql,qr,h) , insr(lc[u],l,m,ql,qr,h);
}

void ins1(int &u,int l,int r,int p,int h){
	if(l == r) return (void)(mxh[u] = h);
	int m = l+r>>1;dt(u);
	p <= m ? ins1(lc[u],l,m,p,h) : ins1(rc[u],m+1,r,p,h);
	upd(u);
}

void ins2(int &u,int l,int r,int p,int h){
	if(l==r) return (void)(mxhr[u] = h);
	int m = l+r>>1;dt(u);
	p <= m ? ins2(lc[u],l,m,p,h) : ins2(rc[u],m+1,r,p,h);
	upd(u);
}

int qry(int u,int l,int r,int p){
	if(l == r) return val[u];
	int m = l+r>>1;dt(u);
	return p <= m ? qry(lc[u],l,m,p) : qry(rc[u],m+1,r,p);
}

int main(){
	
	memset(fz,-1,sizeof fz) , 
	memset(lz,-1,sizeof lz) , 
	memset(rz,-1,sizeof rz);
	read(n),read(q);
	rep(i,1,n-1) read(h[i]);
	h[0] = h[n] = inf;
	Build(rt[0],1,n);
	
	int op,p,x,h;
	rep(i,1,q){
		read(op),read(p),read(x);
		rt[i] = newnode(rt[p]);
		if(op == 0){
			read(h);
			if(qry(rt[i],1,n,x) < h){
				int L = qryl(rt[i],1,n,x-1,h) , R = qryr(rt[i],1,n,x+1,h);
				insf(rt[i],1,n,L+1,R-1,h);
			}
		}
		if(op == 1){
			h = qry(rt[i],1,n,x);
			int L = qryl(rt[i],1,n,x-1,h) , R = qryr(rt[i],1,n,x+1,h);
			insl(rt[i],1,n,x,R-1,h=0),insr(rt[i],1,n,L+1,x,h=0);
		}
		if(op == 2){
			read(h);
			ins1(rt[i],1,n,x,h) , ins2(rt[i],1,n,x+1,h);
		}
		if(op == 3)
			printf("%d\n",qry(rt[i],1,n,x));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值