Codechef:Count on a Treap(线段树)

本文深入探讨了Treap数据结构的原理与实现,详细解释了如何利用Treap进行中序遍历排序,以及如何通过Treap解决特定的深度查询问题。文章提供了完整的代码示例,包括初始化、节点操作、查询最小值、更新节点等关键步骤,适用于需要高效处理排序和查询场景的开发者。

传送门

题解:
Treap的中序遍历是按照键值来排序后的序列,两点的lca为这个序列上区间权值的最小值。

现在只用考虑怎么求深度:
显然往前的一个权值比他大的会成为他的父亲,或者他们中间夹了一个更大的成为他的父亲。 其实就是求上升序列,然后套用常见套路即可。

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pii;

const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&& (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}

const int N=3e5+50;
int n,m,pos[N],ori[N],a[N];
struct query {int op,p,q;} q[N];
map <int,int> mp;

inline void init() {
	n=rd();
	vector <pii> vec;
	for(int i=1;i<=n;i++) {
		int op=rd();
		if(op==0) {
			q[i].op=1; q[i].p=++m;
			pos[m]=rd(); a[m]=rd();
			vec.push_back(pii(pos[m],m));
		} else if(op==1) {
			q[i].op=2; q[i].p=rd();
		} else {
			q[i].op=3; q[i].p=rd(); q[i].q=rd();
		}
	}
	sort(vec.begin(),vec.end());
	for(int i=0;i<m;i++) ori[i+1]=pos[vec[i].second], pos[vec[i].second]=i+1;
}

struct node {
	pii mn;
	int l1,r1,len1;
	int l2,r2,len2;
	inline void init(int v) {
		mn=pii(v,0); 
		l1=r1=l2=r2=v;
		len1=len2=1;
	}
} tr[N*4];
inline int calcl(int k,int l,int r,int lim) {
	if(l==r) return tr[k].mn.first>lim;
	int mid=(l+r)>>1;
	if(tr[k].l1>lim) return tr[k].len1;
	else if(tr[k<<1|1].r1>lim) return tr[k].len1-tr[k<<1|1].len1+calcl(k<<1|1,mid+1,r,lim);
	else return calcl(k<<1,l,mid,lim);
}
inline int calcr(int k,int l,int r,int lim) {
	if(l==r) return tr[k].mn.first>lim;
	int mid=(l+r)>>1;
	if(tr[k].l2>lim) return tr[k].len2;
	else if(tr[k<<1].r2>lim) return tr[k].len2-tr[k<<1].len2+calcr(k<<1,l,mid,lim);
	else return calcr(k<<1|1,mid+1,r,lim);
}
inline void inc(int k,int l,int r,int p,int v) {
	if(l==r) {
		tr[k].init(v); 
		tr[k].mn.second=(v>=0) ? l : -1;
		return;
	} int mid=(l+r)>>1;
	if(p<=mid) inc(k<<1,l,mid,p,v);
	else inc(k<<1|1,mid+1,r,p,v);
	tr[k].mn=max(tr[k<<1].mn,tr[k<<1|1].mn);
	
	tr[k].l1=tr[k<<1|1].l1;
	tr[k].r1=max(tr[k<<1|1].r1,tr[k<<1].r1);
	tr[k].len1=tr[k<<1|1].len1+calcl(k<<1,l,mid,tr[k<<1|1].r1);
	
	tr[k].l2=tr[k<<1].l2;
	tr[k].r2=max(tr[k<<1].r2,tr[k<<1|1].r2);
	tr[k].len2=tr[k<<1].len2+calcr(k<<1|1,mid+1,r,tr[k<<1].r2);
}
inline int ql(int k,int l,int r,int L,int R,int &lim) {
	if(L>R) return 0;
	if(L<=l && r<=R) {
		int len=calcl(k,l,r,lim); 
		lim=max(lim,tr[k].r1);
		return len;
	} int mid=(l+r)>>1;
	if(R<=mid) return ql(k<<1,l,mid,L,R,lim);
	else if(L>mid) return ql(k<<1|1,mid+1,r,L,R,lim);
	else {
		int len=ql(k<<1|1,mid+1,r,L,R,lim);
		len+=ql(k<<1,l,mid,L,R,lim);
		return len;
	}
}
inline int qr(int k,int l,int r,int L,int R,int &lim) {
	if(L>R) return 0;
	if(L<=l && r<=R) {
		int len=calcr(k,l,r,lim);
		lim=max(lim,tr[k].r2);
		return len;
	} int mid=(l+r)>>1;
	if(R<=mid) return qr(k<<1,l,mid,L,R,lim);
	else if(L>mid) return qr(k<<1|1,mid+1,r,L,R,lim);
	else {
		int len=qr(k<<1,l,mid,L,R,lim);
		len+=qr(k<<1|1,mid+1,r,L,R,lim);
		return len;
	}
}
inline pii askmn(int k,int l,int r,int L,int R) {
	if(L<=l && r<=R) return tr[k].mn;
	int mid=(l+r)>>1;
	if(R<=mid) return askmn(k<<1,l,mid,L,R);
	else if(L>mid) return askmn(k<<1|1,mid+1,r,L,R);
	else return max(askmn(k<<1,l,mid,L,R),askmn(k<<1|1,mid+1,r,L,R));
}
inline int askdep(int x) {
	int val=askmn(1,1,m,x,x).first;
	int r=val, t1=ql(1,1,m,1,x-1,r);
	int l=val, t2=qr(1,1,m,x+1,m,l);
	return t1+t2+1;
}
int main() {
	init();
	for(int i=1;i<N;i++) tr[i].mn=pii(-1,-1);
	for(int i=1;i<=n;i++) {
		if(q[i].op==1) {
			inc(1,1,m,pos[q[i].p],a[q[i].p]);
			mp[ori[pos[q[i].p]]]=pos[q[i].p];
		} else if(q[i].op==2) {
			inc(1,1,m,mp[q[i].p],-1);
		} else {
			int l=mp[q[i].p], r=mp[q[i].q];
			if(l>r) swap(l,r);
			int lca=askmn(1,1,m,l,r).second;
			printf("%d\n",askdep(l)+askdep(r)-2*askdep(lca));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值