1 条题解

  • 0
    @ 2025-8-24 22:36:56

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Doubeecat
    关注 LLM4OI 谢谢喵

    搬运于2025-08-24 22:36:56,当前版本为作者最后更新于2022-03-16 16:09:17,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    THUPC2022 初赛 A.最小公倍树

    给定一个点编号在 [L,R][L,R] 范围内的完全图,边 (u,v)(u,v) 的权值为 lcm(u,v)\mathrm{lcm}(u, v),请你求出这张图的最小生成树权值和。

    L,R106,RL105L,R \leq 10^6,R - L \leq 10^5

    解题思路:

    Kruskal 优化建图。

    观察到如果直接建图的时间复杂度是 O((RL)2)\mathcal{O}((R-L)^2 ) 的,无法接受。但是发现在这个过程中,有很多边实际上是可以省略的,接下来来发掘一下这些边的性质。

    首先我们有

    $$\mathrm{lcm}(u, v) = \frac{u\times v}{\mathrm{gcd}(u,v)} $$

    这实际上启发了我们从 gcd(u,v)\mathrm{gcd}(u,v) 的角度进行思考,我们发现,如果将两个有共同因子的点连边,这条边的权值是较小的。观察到 L,R106L,R \leq 10^6,这启发我们去枚举这个因子。同时我们发现,如果对于一个因子 xx 来说,在 [L,R][L,R] 里的数都满足 kx,(k+1)x(k+p)xkx,(k+1)x\dots (k+p)x 这样的形式,显然对于 (k+1)x(k+1)x 及后面的数,向 kxkx 连边是最优秀的,这样我们对于一个因子最多建边数量为 RLx\lfloor\frac{R-L}{x}\rfloor 的。

    而我们可以枚举因子,因子的范围是 [2,R][2,R] ,最后我们建边的数量就应该是 $\lfloor\frac{R-L}{2}\rfloor + \lfloor\frac{R-L}{3}\rfloor + \dots + \lfloor\frac{R-L}{R}\rfloor$ 根据调和级数,这个东西的边数是 O((RL)logR)\mathcal{O}((R-L) \log R) 的,实际上这个上界很松,具体数据会更小,极限数据大概是 10510^5 左右的,建出边后跑 Kruskal 就过了。

    时间复杂度 O((RL)log2(RL))\mathcal{O}((R-L) \log ^2(R-L))

    代码:

    const int N = 1e6 + 10;
    
    int l,r,f[N],buc[N];
    bool vis[N];
    
    ll gcd(ll a,ll b) {
        return !b ? a : gcd(b,a%b);
    }
    
    ll lcm(ll a,ll b) {
        return a / gcd(a,b) * b;
    }
    
    int find(int x) {return f[x] == x ? x : f[x] = find(f[x]);}
    
    struct node {
        int l,r;
        ll w;
        friend inline bool operator < (const node &a,const node &b) {
            return a.w < b.w;
        }
    };
    
    vector <node> edge;
    
    void Kruskal() {
        sort(edge.begin(),edge.end());
        int cnt = 0;
        ll ans = 0;
        for (auto e : edge) {
            int x = e.l,y = e.r;ll w = e.w;
            if (find(x) != find(y)) {
                //printf("%d %d %lld\n",x,y,w);
                f[max(find(x),find(y))] = min(find(x),find(y));
                ans += w;
            }
        }
        printf("%lld\n",ans);
    }
    
    signed main() {
        read(l,r);
        ll ans = 0;
        for (int i = l;i <= r;++i) f[i] = i,buc[i] = 1;
        for (int i = 2;i <= r;++i) {
            //if (vis[i]) continue;
            int cnt = 0,fis = 0;
            
            for (int j = i;j <= r;j += i) {
                if (buc[j] && !fis) fis = j;
                if (buc[j]) edge.push_back((node){fis,j,lcm(fis,j)});
                vis[j] = 1;
            }
    
            if (i >= l) edge.push_back((node){fis,l,lcm(fis,l)});
        }
        Kruskal();
    	return 0;
    }
    
    
    • 1

    信息

    ID
    7562
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者