析合树

给你们一个LCA的9K代码就溜:

#include<bits/stdc++.h>

using namespace std;

const int MX=2000005,MXT=MX<<1,MXSEG=1<<20|1,inf=0x7fffffff;

int n,q;
int p[MX],a[MX];

struct seg_t{
	int ad;
	int mn;
	void add(int d){
		ad+=d;
		if(mn!=inf)mn+=d;
	}
	void set(int d){ // only for leaves
		ad=0,mn=d;
	}
	void push_down(seg_t &l, seg_t &r){
		if(ad){
			l.add(ad),r.add(ad);
			ad=0;
		}
	}
	void push_up(const seg_t &l, const seg_t &r){
		mn=min(l.mn, r.mn);
	}
}; // 前缀+1;前缀-1;区间最值;
struct separationtree_t{
	// private:
	int ip[MX];
	seg_t sg[MXSEG];
	// public
	struct snode_t{
		int fa,rson,pre,typ,l,r;
		snode_t(int _f=0,int _rs=0,int _t=0,int _l=0,int _r=0)
			:fa(_f),rson(_rs),pre(0),typ(_t),l(_l),r(_r){}
		void debug(){
			cerr<<"fa: "<<fa<<" rs: "<<rson<<" p: "<<pre<<" t: "<<typ<<" l: "<<l<<" r: "<<r<<endl;
		}
	}node[MXT];
	int nit,rot;
	void add(int no,int l,int r,int ll,int rr,int d){
		if(ll<=l&&rr>=r)sg[no].add(d);
		else{
			sg[no].push_down(sg[no<<1],sg[no<<1|1]);
			int mid=(l+r)>>1;
			if(ll<=mid)add(no<<1,l,mid,ll,rr,d);
			if(rr>mid)add(no<<1|1,mid+1,r,ll,rr,d);
			sg[no].push_up(sg[no<<1],sg[no<<1|1]);
		}
	}
	int queryL(int no,int l,int r){
		if(l==r)return l;
		else{
			sg[no].push_down(sg[no<<1],sg[no<<1|1]);
			int mid=(l+r)>>1;
			if(sg[no<<1].mn<=1)return queryL(no<<1,l,mid);
			else return queryL(no<<1|1,mid+1,r);
		}
	}
	bool check(int no,int l,int r,int x){
		if(l==r)return sg[no].mn<=1;
		else{
			sg[no].push_down(sg[no<<1],sg[no<<1|1]);
			int mid=(l+r)>>1;
			if(x<=mid)return check(no<<1,l,mid,x);
			else return check(no<<1|1,mid+1,r,x);
		}
	}
	void build(int n, int p[]){
		for(int i=1;i<=n;i++)ip[p[i]]=i;
		static int stk[MX];
		int stc=0;
		nit=n;
		for(int i=1;i<=n;i++){
			//if(i%100==0)cerr<<i<<endl;
			add(1,1,n,1,i,1);
			if(p[i]>1&&ip[p[i]-1]<i)add(1,1,n,1,ip[p[i]-1],-1);
			if(p[i]<n&&ip[p[i]+1]<i)add(1,1,n,1,ip[p[i]+1],-1);
			int L=queryL(1,1,n);
			
			node[i]=snode_t(0,0,0,i,i);
			stk[stc++]=i;
			while(stc>=2&&node[stk[stc-1]].l>L){
				snode_t &x=node[stk[stc-2]],&y=node[stk[stc-1]];
				if(x.rson&&check(1,1,n,node[x.rson].l)){ // 合点
					y.fa=stk[stc-2],y.pre=x.rson;
					x.rson=stk[stc-1];x.r=y.r;x.typ=1;
					--stc;
					continue;
				}
				for(int i=stc-2;i>=0;i--)if(check(1,1,n,node[stk[i]].l)){
					node[++nit]=snode_t(0,stk[stc-1],0,node[stk[i]].l,node[stk[stc-1]].r);
					while(stc>i){
						node[stk[--stc]].fa=nit;
						if(stc>i)node[stk[stc]].pre=stk[stc-1];
					}
					stk[stc++]=nit;
					break;
				}
			}
		}
		rot=stk[0];
	}
	void debug(){
		for(int i=1;i<=nit;i++)node[i].debug();
	}
	snode_t& operator [](const int x){return node[x];}
	const snode_t& operator[](const int x)const{return node[x];}
}sept;
struct tree1_t{
	// 析点、重儿子
	// 单点修改、区间+、区间min
	seg_t sg[MXSEG];
	
	void add(int no,int l,int r,int ll,int rr,int d){
		if(ll<=l&&rr>=r)sg[no].add(d);
		else{
			sg[no].push_down(sg[no<<1],sg[no<<1|1]);
			int mid=(l+r)>>1;
			if(ll<=mid)add(no<<1,l,mid,ll,rr,d);
			if(rr>mid)add(no<<1|1,mid+1,r,ll,rr,d);
			sg[no].push_up(sg[no<<1],sg[no<<1|1]);
		}
	}
	void set(int no,int l,int r,int x,int d){
		if(l==r)sg[no].set(d);
		else{
			sg[no].push_down(sg[no<<1],sg[no<<1|1]);
			int mid=(l+r)>>1;
			if(x<=mid)set(no<<1,l,mid,x,d);
			else set(no<<1|1,mid+1,r,x,d);
			sg[no].push_up(sg[no<<1],sg[no<<1|1]);
		}
	}
	int rangemin(int no,int l,int r,int ll,int rr){
		if(ll<=l&&rr>=r)return sg[no].mn;
		else{
			sg[no].push_down(sg[no<<1],sg[no<<1|1]);
			int mid=(l+r)>>1;
			int ans=inf;
			if(ll<=mid)ans=min(ans,rangemin(no<<1,l,mid,ll,rr));
			if(rr>mid)ans=min(ans,rangemin(no<<1|1,mid+1,r,ll,rr));
			return ans;
		}
	}
	int val(int no,int l,int r,int x){
		if(l==r)return sg[no].mn;
		else{
			sg[no].push_down(sg[no<<1],sg[no<<1|1]);
			int mid=(l+r)>>1;
			if(x<=mid)return val(no<<1,l,mid,x);
			else return val(no<<1|1,mid+1,r,x);
		}
	}
}tsplit,theavy;
struct bst_data_t{
	int mn[2],s;
	bst_data_t():s(0){mn[0]=mn[1]=0;}
	void set(int v){
		s=v;
		mn[0]=mn[1]=min(0,v);
	}
};
bst_data_t operator +(const bst_data_t &l, const bst_data_t &r){
	bst_data_t a;
	a.s=l.s+r.s,a.mn[0]=min(l.mn[0],l.s+r.mn[0]),a.mn[1]=min(l.mn[1]+r.s,r.mn[1]);
	return a;
}
struct bst_t{
	bst_t *s[2];
	int l,r;
	bst_data_t d;
	bst_t():l(0),r(0),d(){
		s[0]=s[1]=NULL;
	}
	bst_data_t rangequery(int ll,int rr){
		if(rr<l||ll>r)return bst_data_t();
		if((ll<=l&&rr>=r)||!s[0])return d;
		else{
			if(rr<=s[0]->r)return s[0]->rangequery(ll,rr);
			else if(ll>s[0]->r)return s[1]->rangequery(ll,rr);
			else return s[0]->rangequery(ll,rr)+s[1]->rangequery(ll,rr);
		}
	}
	void update(int x,int v){
		if(!s[0])d.set(v);
		else{
			if(x<=s[0]->r)s[0]->update(x,v);
			else s[1]->update(x,v);
			d=s[0]->d+s[1]->d;
		}
	}
	void debug(int indent=0){
		if(s[0])s[0]->debug(indent+1);
		for(int i=0;i<indent;i++)cerr<<'\t';
		cerr<<'['<<l<<", "<<r<<']'<<endl;
		if(s[1])s[1]->debug(indent+1);
	}
}tl_pool[MXT*2],*tl_it=tl_pool,*tl[MXT];
struct hld_t{
	// private:
	int sz[MXT];
	// public:
	int top[MXT],hson[MXT];
	int dfn[MXT],dfc;
	hld_t():dfc(0){}
	void dfs(separationtree_t& t, int no){
		sz[no]=1;
		for(int i=t[no].rson;i;i=t[i].pre){
			dfs(t, i);
			sz[no]+=sz[i];
			hson[no]=(hson[no]&&sz[hson[no]]>=sz[i]?hson[no]:i);
		}
	}
	void decomp(separationtree_t& t, int no, int _top){
		top[no]=_top;dfn[no]=++dfc;
		if(hson[no]){
			decomp(t, hson[no], _top);
			for(int i=t[no].rson;i;i=t[i].pre)if(i!=hson[no]){
				decomp(t, i, i);
			}
		}
	}
	void build(separationtree_t& t){
		dfs(t, t.rot);
		decomp(t, t.rot, t.rot);
	}
	int querychain(separationtree_t& t, tree1_t& sg,int x){
		int ans=inf;
		while(x){
			ans=min(ans,sg.rangemin(1,1,t.nit,dfn[top[x]],dfn[x]));
			x=t[top[x]].fa;
		}
		return ans;
	}
	void maintain_hson(separationtree_t& t, int x){
		tl[x]->update(t[hson[x]].l,tsplit.val(1,1,t.nit,dfn[hson[x]]));
	}
	int query(separationtree_t& t,int x,int s){ // 关键点位于x的子树s中的答案
		maintain_hson(t, x);
		return tsplit.val(1,1,t.nit,dfn[s])+tl[x]->rangequery(1,t[s].l-1).mn[1]+tl[x]->rangequery(t[s].r+1,inf).mn[0];
	}
	void addchain(separationtree_t& t,tree1_t& sg,int x,int v){
		while(x){
			sg.add(1,1,t.nit,dfn[top[x]],dfn[x],v);
			x=t[top[x]].fa;
		}
	}
	void updheavy(separationtree_t& t,tree1_t& sg,int x,int v){
		// 注意:v是增量. 但由于query函数只依赖于tsplit,这足够了
		while(x){
			if(top[x]!=x)sg.add(1,1,t.nit,dfn[top[x]]+1,dfn[x],v);
			if(t[top[x]].fa){
				// 更新t[top[x]].fa的轻儿子……
				tl[t[top[x]].fa]->update(t[top[x]].l,tsplit.val(1,1,t.nit,dfn[top[x]]));
				// 更新t[top[x]].fa的重儿子……
				if(t[t[top[x]].fa].typ==1)
					sg.set(1,1,t.nit,dfn[hson[t[top[x]].fa]],query(t,t[top[x]].fa,hson[t[top[x]].fa]));
			}
			x=t[top[x]].fa;
		}
	}
	int querylight(separationtree_t& t,int x){
		int ans=inf;
		while(x){
			if(t[top[x]].fa&&t[t[top[x]].fa].typ==1){
				ans=min(ans,query(t,t[top[x]].fa,top[x]));
			}
			x=t[top[x]].fa;
		}
		return ans;
	}
	void ini_heavy(separationtree_t& t,tree1_t& sg){
		for(int i=1;i<=t.nit;i++)if(top[i]==i||t[t[i].fa].typ==0)sg.set(1,1,t.nit,dfn[i],inf);
	}
	void debugpoints(separationtree_t& t, tree1_t& sg){
		for(int i=1;i<=t.nit;i++)cerr<<sg.val(1,1,t.nit,dfn[i])<<' ';
		cerr<<endl;
	}
}hld;
int query(int x){
	int ans=inf;
	ans=min(ans,hld.querychain(sept,tsplit,x)); // 查询析点
	// cerr<<"k1: "<<ans<<endl;
	ans=min(ans,hld.querychain(sept,theavy,x)); // 查询作为重儿子的祖先
	// cerr<<"k2: "<<ans<<endl;
	ans=min(ans,hld.querylight(sept,x)); // 查询作为轻儿子的祖先
	return ans;
}
void update(int x,int v){
	hld.addchain(sept,tsplit,x,v-a[x]);
	// cerr<<"add chain"<<endl;
	hld.updheavy(sept,theavy,x,v-a[x]);
	// cerr<<"upd heavy"<<endl;
	a[x]=v;
}
bst_t* bst_build(int* l,int* r){
	bst_t* t=tl_it++;
	if(r==l+1){
		t->l=sept[*l].l,t->r=sept[*l].r;
	}else{
		int* m=l+((r-l)>>1);
		t->s[0]=bst_build(l,m),t->s[1]=bst_build(m,r);
		t->l=t->s[0]->l,t->r=t->s[1]->r;
	}
	return t;
}
void ini(){
	for(int i=1;i<=sept.nit;i++)if(sept[i].rson){
		static int q[MX];int qc=0;
		for(int j=sept[i].rson;j;j=sept[j].pre)q[qc++]=j;
		reverse(q,q+qc);
		tl[i]=bst_build(q,q+qc);
		// cerr<<"tl: "<<i<<endl;
		// tl[i]->debug();
	}
	// cerr<<"bst built"<<endl;
	hld.ini_heavy(sept,theavy);
	// cerr<<"ini heavy"<<endl;
	for(int i=1;i<=n;i++){
		int t=a[i];a[i]=0;
		update(i,t);
	}
}
void solve(){
	for(int i=1;i<=q;i++){
		int op; scanf("%d",&op); //cin>>op;
		if(op==1){
			int u; scanf("%d",&u); // cin>>u;
			printf("%d\n",query(u));
//			cout<<query(u)<<endl;
		}else if(op==2){
			int i,a;
			scanf("%d%d",&i,&a);
			//cin>>i>>a;
			update(i,a);
			// hld.debugpoints(sept, tsplit);
			// hld.debugpoints(sept, theavy);
		}
	}
}
int main(){
//	ios::sync_with_stdio(false);
	scanf("%d",&n);
//	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",p+i);//cin>>p[i];
	for(int i=1;i<=n;i++) scanf("%d",a+i);//cin>>a[i];
	sept.build(n, p);
	// cerr<<"sept"<<endl;
	// sept.debug();
	hld.build(sept);
	// cerr<<"hld"<<endl;
	ini();
	// cerr<<"ini"<<endl;
	// hld.debugpoints(sept, tsplit);
	// hld.debugpoints(sept, theavy);
	scanf("%d",&q); //cin>>q;
	solve();
	return 0;
}

前置技能:
快速计算一个排列中所有的 排序后后相邻两数后一数比前一数大一 的区间。
Codeforces 997 E 题解(不是我写的)
两个单调栈求最值配合线段树维护 M a x − M i n − L e n Max - Min - Len MaxMinLen的最小值。
为0代表有该种区间。
可以套上求历史值和的线段树来解决CF上的题。
AC Code;

#include<bits/stdc++.h>
#define maxn 400005
#define LL long long
#define lc now<<1
#define rc lc|1
using namespace std;

int n,p[maxn],Q,l[maxn],r[maxn];
vector<int>G[maxn];

int mn[maxn<<3],cnt[maxn<<3],ad[maxn<<3],ada[maxn<<3];
LL ret[maxn],ans[maxn<<3];

void dtadd(int now){
	if(ad[now]){
		mn[now] += ad[now] , ad[lc] += ad[now] , ad[rc] += ad[now];
		ad[now] = 0;
	}
}

void dt(int now){
	if(ad[now]){
		mn[now] += ad[now] , ad[lc] += ad[now] , ad[rc] += ad[now];
		ad[now] = 0;
	}
	if(ada[now]){
		ans[now] += 1ll * cnt[now] * ada[now];
		dtadd(lc) , dtadd(rc);
		if(mn[now] == mn[lc]) ada[lc] += ada[now];
		if(mn[now] == mn[rc]) ada[rc] += ada[now];
		ada[now] = 0;
	}
}

void upd(int now){
	mn[now] = min(mn[lc] , mn[rc]);
	ans[now] = ans[lc] + ans[rc];
	cnt[now] = (mn[now] == mn[lc]) * cnt[lc] + (mn[now] == mn[rc]) * cnt[rc];
}

void Build(int now,int l,int r){
	if(l == r){
		cnt[now] = 1;
		return;
	}
	int mid = (l+r) >> 1;
	Build(lc,l,mid) , Build(rc,mid+1,r);
	upd(now);
}

void Insert(int now,int l,int r,int ql,int qr,int x,int x2){
	dt(now);
	if(l>qr || ql>r) return;
	if(ql<=l && r<=qr){
		ad[now] += x;
		if(!mn[now]) ada[now] += x2;
		dt(now);
		return;
	}
	int mid = (l+r) >> 1;
	Insert(lc,l,mid,ql,qr,x,x2) , Insert(rc,mid+1,r,ql,qr,x,x2);
	upd(now);
}

LL Query(int now,int l,int r,int ql,int qr){
	dt(now);
	if(l>qr||ql>r) return 0;
	if(ql<=l && r<=qr)
		return ans[now];
	int mid = (l+r) >> 1;
	return Query(lc,l,mid,ql,qr) + Query(rc,mid+1,r,ql,qr);
}
int q[2][maxn],tp[2];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&p[i]);
	scanf("%d",&Q);
	for(int i=1;i<=Q;i++){
		scanf("%d%d",&l[i],&r[i]);
		G[r[i]].push_back(i);
	}
	
	Build(1,1,n);
	for(int i=1;i<=n;i++){
		for(;tp[0] && p[q[0][tp[0]]] < p[i];) Insert(1,1,n,q[0][tp[0]-1]+1,q[0][tp[0]],p[i]-p[q[0][tp[0]]],0),tp[0]--;
		for(;tp[1] && p[q[1][tp[1]]] > p[i];) Insert(1,1,n,q[1][tp[1]-1]+1,q[1][tp[1]],p[q[1][tp[1]]]-p[i],0),tp[1]--;
		q[0][++tp[0]] = q[1][++tp[1]] = i;
		Insert(1,1,n,1,i,0,1);
		for(int u:G[i])
			ret[u] = Query(1,1,n,l[u],i);
		Insert(1,1,n,1,i,-1,0);
	} 
	
	for(int i=1;i<=Q;i++) 
		printf("%lld\n",ret[i]);
}

然后学习简便易懂的 O ( n log ⁡ n ) O(n\log n) O(nlogn)构造析合树的方法

就是在数列构成的析合树森林中,后面插入新数,新数看做一个连续段(或没有儿子的特殊合点)(采用上述博客中的定义)。
如果前面的点是合点,你就可能可以变成它的儿子(和他的所有儿子(也就是它自己所代表的连续段)合并,用线段树判断)
如果不能,但是你可以和它代表的连续端合并,就新建一个合点,这个合点的儿子是它和你,再让这个合点如一个新点般往前合并。
如果都不能,就新建一个析点,这个析点的儿子是它和你,往前拓展,
如果不能拓展了,就有两种:
1.这个析点代表的区间是一个连续段,那么就退出,什么都不用管。
2.这个析点代表的区间不是一个连续段,删掉析点,这个新点是不能合并的 ,成为森林中的一颗树。

这个2代表失配,每次失配是 O ( n ) O(n) O(n)的,所以可能复杂度变为 O ( n 2 ) O(n^2) O(n2)
为了避免这个情况,我们用线段树求出新点往左最远可以到x使得[x,now location]是一个连续段。
只要枚举的前面析合树左端点小于这个x,我们就不合并了,反正都一定会失配。

这样复杂度就是 O ( n log ⁡ n ) O(n\log n) O(nlogn)

细心的读者可以发现这个方法只适用与数列为排列的情况。
数列为任意数的情况就可以参考LCA大佬的9K代码,尽情享受代码的魅力。
LCA题解:

比较容易的实现是在从左到右扫描右端点时同时维护一棵线段树表示当前每个左端点对应的区
间内元素个数减权值相邻关系对数的值。连续段此值为 ,否则 。那么可以维护区间最小值来支持查询以当前位
置为右端点的连续段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值