[USACO21DEC] Connecting Two Barns S(连接两个牛棚)

题目描述

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\) 可以相互到达所需要的最小花费。

输入格式

每个测试用例的输入包含 \(T\) 个子测试用例(\(1\le T\le 20\)),所有子测试用例必须全部回答正确才能通过整个测试用例。

输入的第一行包含 \(T\),之后是 \(T\) 个子测试用例。

每个子测试用例的第一行包含两个整数 \(N\)\(M\)。以下 \(M\) 行,每行包含两个整数 \(i\)\(j\),表示一条连接两个不同田地 \(i\)\(j\) 的道路。输入保证任何两个田地之间至多只有一条道路,并且所有子测试用例的 \(N+M\) 之和不超过 \(5 \cdot 10^5\)

输出格式

输出 \(T\) 行。第 \(i\) 行包含一个整数,为第 \(i\) 个子测试用例的最小花费。

样例输入

2
5 2
1 2
4 5
5 3
1 2
2 3
4 5

样例输出

2
1

样例解释

  • 第一个子测试用例中,最优的方式是用一条道路连接田地 2 和 3,用一条道路连接田地 3 和 4。
  • 第二个子测试用例中,最优的方式是用一条道路连接田地 3 和 4。不需要第二条道路。

数据范围

  • 测试点 2 满足 \(N \le 20\)
  • 测试点 3-5 满足 \(N \le 10^3\)
  • 测试点 6-10 没有额外限制。

Solution:

因为建造至多两条新道路,故最优解可能会出现下列情况:

  • 不连边:1号节点与n号节点联通
  • 连1条边:1号节点与n号节点不联通,在它们所在的联通块中各选一个点进行连接
  • 连2条边:1号节点与n号节点不联通,在它们所在的联通块中各选一个点与中转块(第3个联通块)中的点进行连接

可以发现,连1条边其实是连2条边的特殊情况:中转块为 1/n号节点的连通块

因此,可以枚举中转块,注意:不是中转点,因为连接 1号节点联通块和n号节点联通块的 可能不是同一节点

Code:

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>

using namespace std;

typedef long long ll;

const int N=1e5+5;
const ll INF=1e18;

int n,m;
int p[N];
ll f[N],g[N];

int find(int x){return x==p[x]?x:p[x]=find(p[x]);}
ll dis(int x,int y){return (ll)(x-y)*(x-y);}

int main()
{
    int T;
    scanf("%d",&T);
    
    while(T--)
    {
        scanf("%d%d",&n,&m);

        memset(p,0,sizeof(p));
        for(int i=1;i<=n;++i)p[i]=i;

        int u,v;
        for(int i=1;i<=m;++i)
        {
            scanf("%d%d",&u,&v);
            p[find(u)]=find(v);
        }
        
        int x=find(1),y=find(n);
        if(x==y)
        {
            puts("0");
            continue;
        }

        vector<int> a,b;
        for(int i=1;i<=n;++i)
        {
            if(find(i)==x)a.push_back(i);
            if(find(i)==y)b.push_back(i);
        }

        memset(f,0x3f,sizeof(f));
        memset(g,0x3f,sizeof(g));

        ll ans=INF;
        for(int i=1;i<=n;++i)
        {
            ll res1=INF,res2=INF;
            int p=lower_bound(a.begin(),a.end(),i)-a.begin();
            if(p<a.size())res1=min(res1,dis(a[p],i));
            if(p>0)res1=min(res1,dis(a[p-1],i));

            p=lower_bound(b.begin(),b.end(),i)-b.begin();
            if(p<b.size())res2=min(res2,dis(b[p],i));
            if(p>0)res2=min(res2,dis(b[p-1],i));

            u=find(i);
            f[u]=min(f[u],res1);
            g[u]=min(g[u],res2);
        }
        for(int i=1;i<=n;++i)
            if(find(i)==i)
                ans=min(ans,f[i]+g[i]);
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2022-10-04 11:27  FighterQ  阅读(156)  评论(0)    收藏  举报