ssoj1018地下组织

4 篇文章 0 订阅

【题意】他要让一些人有联络方式, 使得每个人都可以直接戒者间接的联系其他人。当然, 为了让地下组织保持隐蔽, 建立的联络方式越少越好。 Nc现在有m对可以选择建立的联络方式。 对于每对选择, 都有一个特定的花费。 建立好一个完善的联络方式的总花费, 是选择的每对关系的花费的最大公约数。 当然,建立好一个完善联络方式的方法也还是不唯一。 现在nc想知道, 对于所有的可能的方法, 都会有一个总花费。 他想知道这些总花费的最小公倍数。

【思路】记录图中边权的所有质因数,枚举这些质因数x最为最大公约数的因数,将x的次数转化成克鲁斯卡尔的费用,用克鲁斯卡尔跑一边,x次数最小的就是这条路的最大公因数,乘入ans。

【代码】

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=1003;
const int maxm=100005;
const ll mod=(ll)1<<31;
struct data{
    int u,v,w;
    bool operator<(const data&b)const{
        return w>b.w;
    }
}e[maxm*2],g[maxm*2];
int n,m,pt=0,p[maxm],num,fa[maxm],vst[maxm];
ll ans=1;
inline int get(){
    char c;while(!isdigit(c=getchar()));
    int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
    return v;
}
inline void getprime(){
    memset(vst,0,sizeof(vst));
    for(int t=1;t<=m;++t){
        int c=g[t].w;
        for(int i=2;i*i<=g[t].w;++i)if(c%i==0){
            while(c%i==0)c/=i;
            if(!vst[i])p[++pt]=i;
            ++vst[i];
        }
        if(c>1&&!vst[c])p[++pt]=c;++vst[c];
    }
}
inline void gettot(int x,int pri){//将质因数的次数作为krus的边权
    int t=0;int c=g[x].w;e[++num]=g[x];
    while(c%pri==0)c/=pri,++t;
    e[num].w=t;
}
inline int Find(int x){
    if(fa[x]==x)return fa[x];
    else return (fa[x]=Find(fa[x]));
}
inline int krus(){
    int cnt=0,tme=0;
    sort(e+1,e+1+num);
    for(int i=1;i<=n;++i)fa[i]=i;
    for(int i=1;i<=num;++i){
        int x=Find(fa[e[i].u]);
        int y=Find(fa[e[i].v]);
        if(x!=y)++cnt,fa[y]=x,tme=e[i].w;
    }
    if(cnt==n-1)return tme;
    else return 0;
}
inline ll Pow(int a,int b){
    ll tmp=a,ret=1;
    while(b){
        if(b&1)ret=ret*tmp%mod;
        tmp=tmp*tmp%mod;
        b>>=1;
    }
    return ret;
}
int main(){
    n=get();m=get();
    for(int i=1;i<=m;++i)g[i].u=get(),g[i].v=get(),g[i].w=get();
    getprime();
    int tmp=0;
    for(int i=1;i<=pt;++i)if(vst[p[i]]>=n-1){//枚举质因数
        num=0;
        for(int j=1;j<=m;++j)if(g[j].w%p[i]==0)gettot(j,p[i]);
        if(num>=n-1){
            int t=krus();
            if(t)ans=ans*Pow(p[i],t)%mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值