[机房测试]万花筒

Descrption

初始 \(m\) 条边,如果 \((u,v)\) 在图中,那么将 \(((u+1)\bmod n+1,(v+1)\bmod n+1)\) 也加入图中。求该图的最小生成树,\(1\leq n\leq 10^9,1\leq m \leq 10^5\)

Solution

先考虑暴力求最小生成树,那么一共会有 \(nm\) 条边。但是会发现这些边是有一定规律的。我们将通过 \((u,v)\) 生成的边化为一类。发现如果将这类边全部连起来,会将图化为 \(\gcd(n,abs(u-v))\) 个连通块,连通块内部就不管了,直接减作 \(\gcd(n,abs(u-v))\) 个点。所以可以同时处理一类边的情况。这样就可以做到 \(O(m\log m)\)

#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

struct E{
    int type,w;
    E(int type_=0,int w_=0):type(type_),w(w_){}
    bool operator <(const E &X) const{
        return w<X.w;
    }
};

vector<E> e;
int gcd(int x,int y){
    return y? gcd(y,x%y):x;
}

int main(){
    freopen("kaleidoscope.in","r",stdin);
    freopen("kaleidoscope.out","w",stdout);
    int T=read();
    while(T--){
        ll ans=0; e.clear();
        int n=read(),m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            e.push_back(E(abs(u-v),w));
        }
        sort(e.begin(),e.end());
        int now=n;
        for(E t:e){
            int x=gcd(t.type,now);
            if(x==now) continue;
            ans+=1ll*(now-x)*t.w;
            now=x; if(x==1) break;
        }
        printf("%lld\n",ans);
    }
}
posted @ 2021-10-18 21:50  Kreap  阅读(58)  评论(0编辑  收藏  举报