[机房测试]万花筒
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);
}
}