題目
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號田所在的塊與 \(n\) 號田所在的塊直接相連。
-
通過一個中轉塊,在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;
}