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;
}