【USACO2021 Connecting Two Barns 】题解

题目

Farmer John's farm consists of a set of \(N\) fields \((1 \leq N \leq 10^5)\),
conveniently numbered \(1 \ldots N\). Between these fields are \(M\) bi-directed
paths \((0 \leq M \leq 10^5)\), each connecting a pair of fields.

The farm contains two barns, one in field 1 and the other in field \(N\). Farmer
John would like to ensure that there is a way to walk between the two barns
along some series of paths. He is willing to build up to two new paths to
accomplish this goal. Due to the way the fields are situated, the cost of
building a new path between fields \(i\) and \(j\) is \((i-j)^2\).

Please help Farmer John determine the minimum cost needed such that barns \(1\)
and \(N\) become reachable from each-other.

Farmer John 的农场由 \(N\) 块田地(\(1 \leq N \leq 10^5\))组成,编号为 \(1 \ldots N\)。在这些田地之间有 \(M\) 条双向道路(\(0 \leq M \leq 10^5\)),每条道路连接两块田地。

农场有两个牛棚,一个在田地 1 中,另一个在田地 \(N\) 中。Farmer John 希望确保有一种方式可以沿着一组道路在两个牛棚之间行走。 他愿意建造至多两条新道路来实现这一目标。由于田地的位置因素,在田地 \(i\)\(j\) 之间建造新道路的花费是 \((i-j)^2\)

请帮助 Farmer John 求出使得牛棚 \(1\)\(N\) 可以相互到达所需要的最小花费。

思路

由于只能连两条边,所以只有两种情况。

  1. 在1号田所在的块与 \(n\) 号田所在的块直接相连。

  2. 通过一个中转块,在1号田所在的块与 \(n\) 号田所在的块与这个中转块相连。

那么相连的最小代价是多少呢?

我们可以枚举期中一个块中的每一个点,然后在对应块中二分找出刚好大于和刚好小于的点,算出后去个min值即可。

要注意的是,我们要枚举中转块的点,因为这最多只有 \(n\) 个点,只会二分 \(n\) 次。枚举1号块或 \(n\) 号块的点容易超时。

可以用并查集和set做。

总结

对于题目一些给定且过小的性质,我们可以直接考虑情况。

在统计最小的过程中,可以二分上下界。

对于一些过大的枚举,可以反过来,就会变得很小。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 100010
int n, m, i, j, k; 
set<int>s[N]; 
set<int>::iterator it, jt, kt; 
int f[N], p[N], u, v, a, b, ans, t; 

int fa(int x) { return f[x]==x ? x : f[x]=fa(f[x]); }
int pingfang(int x) { return x*x; }

int len(int x, int y) {
	int ans=1e18; 
	for(it=s[x].begin(); it!=s[x].end(); ++it) {
		jt=s[y].lower_bound(*it); 
		if(jt!=s[y].end()) ans=min(ans, pingfang((*jt)-*it)); 
		if(jt!=s[y].begin()) ans=min(ans, pingfang((*(--jt))-*it)); 
	}
	return ans; 
}

signed main()
{
	scanf("%lld", &t);
	while(t--)
	{
		scanf("%lld%lld", &n, &m);
		for(i=1; i<=n; ++i) f[i]=i; 
		for(i=1; i<=n; ++i) s[i].clear(); 
		for(i=1; i<=m; ++i)
			scanf("%lld%lld", &u, &v), f[fa(u)]=fa(v); 
		for(i=1; i<=n; ++i) s[fa(i)].insert(i); 
		a=f[1]; b=f[n]; ans=len(a, b); 
		for(i=1; i<=n; ++i)
		{
			if(f[i]==a||f[i]==b||f[i]!=i) continue; 
			ans=min(ans, len(i, a)+len(i, b)); 
		}
		printf("%lld\n", ans); 
	}
	return 0;
}
posted @ 2021-12-21 18:25  zhangtingxi  阅读(438)  评论(0编辑  收藏  举报