P8207-[THUPC2022 初賽]最小公倍樹【Kruskal】
阿新 • • 發佈:2022-03-17
正題
題目連結: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'\)
所以我們可以對於一個點\(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; }