题解 [CF1578B] Building Forest Trails

传送门

这个题还是很好做的!(震声
这里提供一个不需要平衡树也不需要启发式合并但跑的比这两个都慢\(O(n\log n)\) 做法

考虑一个连通块内部的边
容易发现可以等效成 这个连通块中的所有点构成的凸多边形 的边
那么考虑连通两个点会使得若干个连通块合并
发现合并次数和维护凸多边形所需的边的变化次数都是 \(O(n)\)
那么来康康怎么维护这个东西
将环展开为链,对每个点维护 \(pre1_i\) 为这个点所在凸多边形上的编号小于这个点的前驱(若没有则 \(pre1_i=i\)
若这个点的后继为凸多边形上编号最小的点,那么将 \(pre2_i\) 设为其后继,否则 \(pre2_i=i\)
对称地维护 \(suf_1\)\(suf_2\)
image
事实上就是想维护出展开后的结果啦
然后发现添加一条边可能跨过若干条当前维护的边
将所有涉及到的边的端点拎出来单独处理
发现将这些点单独排序后相邻点在新多边形上一定有边(除了 \((最小的点, 最大的点)\) 这条边)
那么利用这些边更新涉及到的点的前驱后继,特判一下最小的点和最大的点之间是否有边即可
要怎么找到新添加的边 \((u, v)(u<v)\) 跨过的边呢?
发现就是 \(\{(i, pre1/2_i)\mid i\in[u, v]\and pre1/2_i\leqslant u\}\)\(\{(i, suf1/2_i)\mid i\in[u, v]\and suf1/2_i\geqslant v\}\)
那么用一棵线段树在这个区间上若满足条件就向下递归即可
因为每跨过一条边就会导致两个连通块的合并,所以取出的总点数是 \(O(n)\)
所以最终复杂度是 \(O(n\log n)\) 的,但是带了若干常数,可能需要比较精细的实现
笔者通过将线段树的单点修改部分改为非递归实现通过了本题

另附两组数据
8 6
1 6 4
1 6 8
1 7 2
1 4 2
1 5 3
2 5 6
10 6
1 5 10
1 3 7
1 6 9
1 8 1
1 2 4
2 2 8

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int dsu[N], sta[N*8], top;
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}

struct seg1{
	#define tl(p) tl[p]
	#define tr(p) tr[p]
	int tl[N<<2], tr[N<<2], minn[N<<2], mini[N<<2], pos[N];
	inline void pushup(int p) {
		minn[p]=min(minn[p<<1], minn[p<<1|1]);
		mini[p]=minn[p]==minn[p<<1]?mini[p<<1]:mini[p<<1|1];
	}
	void build(int p, int l, int r) {
		tl(p)=l; tr(p)=r;
		if (l==r) {minn[p]=mini[p]=l; pos[l]=p; return ;}
		int mid=(l+r)>>1;
		build(p<<1, l, mid);
		build(p<<1|1, mid+1, r);
		pushup(p);
	}
	void upd(int t, int p, int val) {for (minn[p=pos[p]]=val,p>>=1; p; p>>=1) pushup(p);}
	void chkmin(int t, int p, int val) {for (p=pos[p],minn[p]=min(minn[p], val),p>>=1; p; p>>=1) pushup(p);}
	void query(int p, int l, int r, int lim) {
		if (minn[p]>lim) return ;
		if (tl(p)==tr(p)) {sta[++top]=minn[p]; sta[++top]=tl(p); return ;}
		int mid=(tl(p)+tr(p))>>1;
		if (l<=mid) query(p<<1, l, r, lim);
		if (r>mid) query(p<<1|1, l, r, lim);
	}
}pre1, pre2;

struct seg2{
	#define tl(p) tl[p]
	#define tr(p) tr[p]
	int tl[N<<2], tr[N<<2], maxn[N<<2], maxi[N<<2], pos[N];
	inline void pushup(int p) {
		maxn[p]=max(maxn[p<<1], maxn[p<<1|1]);
		maxi[p]=maxn[p]==maxn[p<<1]?maxi[p<<1]:maxi[p<<1|1];
	}
	void build(int p, int l, int r) {
		tl(p)=l; tr(p)=r;
		if (l==r) {maxn[p]=maxi[p]=l; pos[l]=p; return ;}
		int mid=(l+r)>>1;
		build(p<<1, l, mid);
		build(p<<1|1, mid+1, r);
		pushup(p);
	}
	void upd(int t, int p, int val) {for (maxn[p=pos[p]]=val,p>>=1; p; p>>=1) pushup(p);}
	void chkmax(int t, int p, int val) {for (p=pos[p],maxn[p]=max(maxn[p], val),p>>=1; p; p>>=1) pushup(p);}
	void query(int p, int l, int r, int lim) {
		if (maxn[p]<lim) return ;
		if (tl(p)==tr(p)) {sta[++top]=maxn[p]; sta[++top]=tl(p); return ;}
		int mid=(tl(p)+tr(p))>>1;
		if (l<=mid) query(p<<1, l, r, lim);
		if (r>mid) query(p<<1|1, l, r, lim);
	}
}suf1, suf2;

signed main()
{
	n=read(); m=read();
	pre1.build(1, 1, n); pre2.build(1, 1, n);
	suf1.build(1, 1, n); suf2.build(1, 1, n);
	for (int i=1; i<=n; ++i) dsu[i]=i;
	for (int i=1,u,v; i<=m; ++i) {
		// cout<<"i: "<<i<<endl;
		if (read()&1) {
			u=read(); v=read(); top=0;
			if (u>v) swap(u, v);
			if (find(u)==find(v)) continue;
			pre1.query(1, u, v, u); pre2.query(1, u, v, u);
			suf1.query(1, u, v, v); suf2.query(1, u, v, v);
			sort(sta+1, sta+top+1);
			top=unique(sta+1, sta+top+1)-sta-1;
			// cout<<"sta: "; for (int j=1; j<=top; ++j) cout<<sta[j]<<' '; cout<<endl;
			for (int j=1; j<top; ++j) dsu[find(sta[j])]=find(sta[j+1]);
			for (int j=1; j<top; ++j) pre1.upd(1, sta[j+1], sta[j]), pre2.upd(1, sta[j], sta[j]);
			for (int j=2; j<=top; ++j) suf1.upd(1, sta[j-1], sta[j]), suf2.upd(1, sta[j], sta[j]);
			pre2.chkmin(1, sta[top], sta[1]);
			suf2.chkmax(1, sta[1], sta[top]);
			// cout<<"pre1: "; for (int j=1; j<=n; ++j) cout<<pre1[j]<<' '; cout<<endl;
			// cout<<"pre2: "; for (int j=1; j<=n; ++j) cout<<pre2[j]<<' '; cout<<endl;
		}
		else printf("%d", find(read())==find(read()));
	}
	printf("\n");
	
	return 0;
}
posted @ 2022-05-18 21:26  Administrator-09  阅读(41)  评论(1编辑  收藏  举报