1 条题解

  • 0
    @ 2025-8-24 22:34:58

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 望月Asta
    语言的难产->语言的便秘

    搬运于2025-08-24 22:34:58,当前版本为作者最后更新于2021-12-25 21:24:39,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题意

    给出一个 nn 个点 mm 条边,编号从 11nn 的无向图,可以以 (ij)2(i - j)^2 的代价在任意的 i,ji,j 之间连一条边。

    现在可以进行不超过两次连边操作,求使得 11nn 两点连通的最小代价。

    解法

    首先不超过两次连边一定是以下几种形式中的一种 :

    • 1,n1,n 初始即连通,不需连边。

    • 11nn 分别所在的连通分量中找出差值最小的两个点连边,共连一条边。

    • 11nn 分别所在的连通分量和第三个连通分量相连,共连两条边。

    可以发现都是把某个点所在的连通分量与 11nn 连通的过程。

    fif_i 表示将点 ii 所在的连通分量与 11 连接的最小代价,gig_i 表示将点 ii 所在的连通分量与 nn 连接的最小代价。

    那么答案为 : mini=1n{fi+gi}\min_{i = 1}^{n} \{f_i + g_i\}

    然后考虑如何求出 ffgg

    首先可以求出初始与 1,n1,n 在同一连通分量的点的集合,记为 FFGG

    对于一个点 uu,在 F,GF,G 中分别二分查找得到与其差值最小的点并尝试更新最小值。

    时间复杂度 O(nlogn)\mathcal{O} (n \log n)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    typedef long long ll;
    constexpr int N = 100005;
    
    ll f[N],g[N];
    int F[N],G[N],fa[N];
    
    int find(int x) {
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    
    void solve() {
        int n,m;
        scanf("%d %d",&n,&m);
        memset(f,127,sizeof(f));
        memset(g,127,sizeof(g));
        for(int i = 1;i <= n;++i)
            fa[i] = i;
        for(int i = 1;i <= m;++i) {
            int u,v;
            scanf("%d %d",&u,&v);
            u = find(u),v = find(v);
            if(u == v) continue;
            fa[u] = v;
        }
        int r1 = find(1),rn = find(n);
        f[r1] = 0,g[rn] = 0;
        int cntF = 0,cntG = 0;
        for(int i = 1;i <= n;++i) {
            int u = find(i);
            if(u == r1) F[++cntF] = i;
            else if(u == rn) G[++cntG] = i;
        }
        for(int i = 1;i <= n;++i) {
            int u = find(i);
            if(u != r1) {
                int pre = std::upper_bound(F + 1,F + cntF + 1,i) - F - 1;
                f[u] = std::min(f[u],(ll)(i - F[pre]) * (i - F[pre]));
                if(pre < cntF) {
                    ++pre;
                    f[u] = std::min(f[u],(ll)(i - F[pre]) * (i - F[pre]));
                }
            }
            if(u != rn) {
                int nxt = std::upper_bound(G + 1,G + cntG + 1,i) - G;
                g[u] = std::min(g[u],(ll)(i - G[nxt]) * (i - G[nxt]));
                if(nxt > 1) {
                    --nxt;
                    g[u] = std::min(g[u],(ll)(i - G[nxt]) * (i - G[nxt]));
                }
            }
        }
        ll ans = (ll)(n - 1ll) * (n - 1ll);
        for(int i = 1;i <= n;++i) {
            int u = find(i);
            ans = std::min(ans,f[u] + g[u]);
        }
        printf("%lld\n",ans);
    }
    
    int main() {
        int T;
        scanf("%d",&T);
        while(T--) solve();
        return 0;
    }
    
    
    • 1

    信息

    ID
    7365
    时间
    1000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者