• P8207[THUPC2022 初赛]最小公倍树【Kruskal】


    正题

    题目链接:https://www.luogu.com.cn/problem/P8207


    题目大意

    有编号为\([L,R]\)区间的点,连接两个点\(x,y\)边权的为\(LCM(x,y)\),求这张图的最小生成树。

    \(1\leq L\leq R\leq 10^6,R-L\leq 10^5\)


    解题思路

    我们有一个结论: 对于张图\(G\)中的一个生成子图\(E\)\(E\)之中的一条边\(k\)如果不在\(E\)最小生成树中,那么\(k\)肯定也不在\(G\)的最小生成树中。

    那么我们考虑找一些可能是答案的边出来跑最小生成树。

    对于一个\(i\)我们提取出所有它倍数的点,对于点\(ik\)来说它肯定不会去连接某个\(ik'\)如果存在另一个更小的\(ik''\)的话,因为这条边显然不在这张图的最小生成树中。

    所以我们可以对于一个点\(x\)的每个约数\(d\),我们只连接一个最小的\(d\times k\),然后把这些边拿出来跑\(Kruskal\)就好了。

    时间复杂度:\(O(n\log^2 n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    struct node{
    	ll x,y,w;
    }e[11000000];
    ll L,R,ans,m,fa[1100000];
    bool cmp(node x,node y)
    {return x.w<y.w;}
    ll find(ll x)
    {return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
    signed main()
    {
    	scanf("%lld%lld",&L,&R);
    	for(ll i=1;i<=R;i++){
    		for(ll j=i;j<=R;j+=i){
    			if(j>=L){
    				ll p=(L+i-1)/i*i;
    				if(p==j)continue;
    				e[++m]=(node){j,p,j*p/i};
    			}
    		}
    	}
    	sort(e+1,e+1+m,cmp);
    	for(ll i=L;i<=R;i++)fa[i]=i;
    	for(ll i=1;i<=m;i++){
    		ll x=find(e[i].x),y=find(e[i].y);
    		if(x==y)continue;
    		ans+=e[i].w;fa[x]=y;
    	}
    	printf("%lld\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    hdu2476
    zoj3469 区间dp好题
    区间dp好题cf149d 括号匹配
    cf1108e 线段树区间更新+扫描线
    完全背包记录路径poj1787 好题
    cf1104d二分+数学
    01背包专题
    hdu1069线性dp
    有源汇的上下界最大流
    有源汇的上下界最大流
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16018767.html
Copyright © 2020-2023  润新知