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;
}
浙公网安备 33010602011771号