CF1578B Building Forest Trails 题解

题目在一个环上连若干条边,两条边交叉也可互相抵达。
支持两个操作。
1.给出\(x\)\(y\)。连接\(x\),\(y\)
2.给出\(x\)\(y\)。求出他们是否在一个联通块内。

首先,我们容易想到断环为链,那么我们要判断的就是给出\((x,y)\),找出之前是否有一条边\((a,b)\)。使得\(a \le x \le b \le y\)\(x \le a \le y \le b\)(交叉了),经典问题,引入一个新变量\(h\),\(h_i\)表达\(i\)点上头经过了多少条边(同一个联通块内只保留最外的若干条即可)。有一个很有意思的结论,\(|h_i-h_{i+1}| \le 1\)。(假设他是2,那么从\(i+1\)射出了两条线到\(i\)之前,靠里的就可以删掉了。)

分类讨论,以下默认\(x \le y\)

1.\(h_x > h_y\),一定有一条边\((a,b)\)满足\(a \le x \le b \le y\)
2.\(h_x < h_y\),一定有一条边\((a,b)\)满足\(x \le a \le y \le b\)
3.\(h_x == h_y\),尝试同一寻找节点\(p\),如果\(p \le x\),那么就还有交叉,否则直接连接\((x,y)\)即可。

运用线段树可以简单的维护,就不详细讲了(能写到这里的肯定都会吧……)。

代码:

#include <bits/stdc++.h>
#define int long long
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)/2)
using namespace std;
const int maxn=2e5+10;
int n,m,op,x,y,mn[maxn<<2],maxx[maxn],lazy[maxn<<2];
int fa[maxn],lin1,lin2,now,minn[maxn];
int find(int q){
	if(fa[q]==q){
		return q;
	}
	fa[q]=find(fa[q]);
	return fa[q];
}
void push_down(int id){
	if(lazy[id]){
		lazy[lid]+=lazy[id];
		lazy[rid]+=lazy[id];
		mn[lid]+=lazy[id];
		mn[rid]+=lazy[id];
		lazy[id]=0;
	}
	return;
}
void push_up(int id){
	mn[id]=min(mn[lid],mn[rid]);
}
int queryl(int id,int l,int r,int q,int w){
	if(mn[id]>=w){
		return n+1;
	}
	if(l==r){
		return l;
	}
	int res=n+1;
	push_down(id);
	if(q<=mid){
		res=min(res,queryl(lid,l,mid,q,w));
	}
	if(res==n+1){
		res=min(res,queryl(rid,mid+1,r,q,w));
	}
	return res;
}
int queryr(int id,int l,int r,int q,int w){
	if(mn[id]>=w){
		return 0;
	}
	if(l==r){
		return l;
	}
	int res=0;
	push_down(id);
	if(mid<q){
		res=max(res,queryr(rid,mid+1,r,q,w));
	}
	if(res==0){
		res=max(res,queryr(lid,l,mid,q,w));
	}
	return res;
}
void add(int id,int l,int r,int q,int w,int qw){
	if(q<=l&&r<=w){
		lazy[id]+=qw;
		mn[id]+=qw;
		return;
	}
	push_down(id);
	if(q<=mid){
		add(lid,l,mid,q,w,qw);
	}
	if(w>mid){
		add(rid,mid+1,r,q,w,qw);
	}
	push_up(id);
	return;
}
int query(int id,int l,int r,int q){
	if(l==r){
		return mn[id];
	}
	push_down(id);
	if(q<=mid){
		return query(lid,l,mid,q);
	}
	else{
		return query(rid,mid+1,r,q);
	}
}
void merge(int q,int w){
	q=find(q);
	w=find(w);
	if(maxx[q]<minn[w]){
		add(1,1,n,maxx[q]+1,minn[w]-1,1);
	}
	else{
		add(1,1,n,max(minn[q],minn[w]),min(maxx[q],maxx[w]),-1);
	}
	fa[q]=w;
	minn[w]=min(minn[q],minn[w]);
	maxx[w]=max(maxx[q],maxx[w]);
	return;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		fa[i]=i;
		minn[i]=i;
		maxx[i]=i;
	}
	while(m--){
		cin>>op>>x>>y;
		if(op==1){
			if(x>y){
				swap(x,y);
			}
			lin1=query(1,1,n,x);
			lin2=query(1,1,n,y);
			while(find(x)!=find(y)){
				if(lin1>lin2){
					merge(x,queryl(1,1,n,x,lin1));
					lin1--;
				}
				else if(lin1<lin2){
					merge(queryr(1,1,n,y,lin2),y);
					lin2--;
				}
				else{
					now=queryl(1,1,n,x,lin1);
					if(now<y){
						merge(x,now);
						lin1--;
					}
					else{
						merge(x,y);
					}
				}
			}
		}
		else{
			x=find(x);
			y=find(y);
			if(x==y){
				cout<<1;
			}
			else{
				cout<<0;
			}
		}
	}
	return 0;
}
posted @ 2025-04-29 07:52  特别之处  阅读(21)  评论(0)    收藏  举报