Luogu7991 solution

Problem

给定 \(n\) 个点,问用至多 \(2\) 条路径连接点 \(1\)\(N\) 最小代价。

link->https://www.luogu.com.cn/problem/P7991

Solution

看见这题第一反应:并查集

题中,提到
他愿意建造至多两条新道路来实现这一目标”,那么,也就是说,对于这题,有三种可能解法:

  • 连接 \(0\) 条路,当且仅当田地 \(1\) 和田地 \(N\) 处于同一连通分量。
  • 连接 \(1\) 条路,直接连接田地 \(1\) 和田地 \(N\),使之处于同一连通分量。
  • 连接 \(2\) 条路,通过一连通分量 \(mid\) 间接连接田地 \(1\) 和田地 \(N\),使之处于同一连通分量。

对于一联通分量,我们可以考虑用 vector 将其存起来。

每次考虑连通分量 \(i\)\(x\) 点去连接连通分量 \(j\) 中的哪个 \(y\) 点时,我们可以通过二分查找 lower_bound 去搜索离 \(x\)\(j\) 中最近点。

细节较多,详见代码。

Code

#include<bits/stdc++.h>
#define int long long //十年OI一场空,不开longlong见祖宗
#define pd push_back
using namespace std;
const int N=1e5+5;
int fa[N],n,T,m;
int GetRoot(int x) { //并查集路径压缩(可以加上按轶合并)
	if(x==fa[x])
		return x;
	return fa[x]=GetRoot(fa[x]);
}
void Union(int x,int y) { //合并两个连通分量
	fa[GetRoot(x)]=fa[GetRoot(y)];
}
signed main() {
	scanf("%lld",&T);
	while(T--) {
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=n;i++) fa[i]=i;
		while(m--) {
			int u,v;
			scanf("%lld%lld",&u,&v);
			Union(u,v);
		}
		for(int i=1;i<=n;i++) GetRoot(i); 注意有些孩子可能还没有找到祖宗
		vector<int> vec[N]; //vector存连通分量
		for(int i=1;i<=n;i++) {
			if(fa[1]==fa[i]) vec[0].pd(i);
			else if(fa[n]==fa[i]) vec[n].pd(i);
			else vec[fa[i]].pd(i);
		}
//		for(int i=1;i<=n;i++) {
//			if(vec[i].empty()) continue;
//			printf("fa=%lld's list=[",i);
//			for(int j=0;j<vec[i].size();j++)
//				printf(" %lld",vec[i][j]);
//			puts("]");
//		}
		if(fa[1]==fa[n]) { //无需连接
			puts("0"); continue;
		}
		int ans=1e10;
		for(int i=0;i<vec[0].size();i++) { //直接连接
			int x=lower_bound(vec[n].begin(),vec[n].end(),vec[0][i])-vec[n].begin();
			if(x!=vec[n].size()) ans=min(ans,(vec[n][x]-vec[0][i])*(vec[n][x]-vec[0][i]));
			if(x!=0) ans=min(ans,(vec[0][i]-vec[n][x-1])*(vec[0][i]-vec[n][x-1]));
		}
		for(int i=2;i<n;i++) { //间接连接
			int a=1e5,b=1e5;
			if(vec[i].empty()) continue;
			for(int j=0;j<vec[i].size();j++) {
				int x=lower_bound(vec[0].begin(),vec[0].end(),vec[i][j])-vec[0].begin();
				int y=lower_bound(vec[n].begin(),vec[n].end(),vec[i][j])-vec[n].begin();
				if(x!=vec[0].size()) a=min(a,vec[0][x]-vec[i][j]);
				if(y!=vec[n].size()) b=min(b,vec[n][y]-vec[i][j]);
				if(x!=0) a=min(a,vec[i][j]-vec[0][x-1]);
				if(y!=0) b=min(b,vec[i][j]-vec[n][y-1]);
			}
			if(a==1e5||b==1e5) continue;
			ans=min(ans,a*a+b*b);
		}
		printf("%lld\n",ans);
	}
	return 1;
}
posted @ 2022-08-02 08:42  lsj2009  阅读(73)  评论(0)    收藏  举报