• [JZOJ 5888] [NOIP2018模拟9.29] GCD生成树 解题报告 (最大生成树+公约数)


    题目链接:

    http://172.16.0.132/senior/#main/show/5888

    题目:

    题解:

    思路是这样的:两个数的最大公约数一定不会比这两个数的任意一个数大。因此我们把权值相等的看成一个点,先把这些点连起来算上贡献

    考虑kruskal的做法,我们从大到小枚举边权,其实就是我们从大到小枚举最大公约数。假设当前枚举到i,我们再枚举$k_1$,$k_2$,判断$k_1i$,$k_2i$是否存在,若是存在再判断二者是否连通,如果没有连通就连起来算上i的贡献。或许有的人会想这样$2i$,$4i$的贡献不就算小了吗?注意我们是从大到小枚举,这样的情况在枚举$2i$的时候就已经连边了

    但是这样枚举两个$k$会T掉,怎么处理呢?实际上我就是要把这些倍数都放到一个连通块里,于是每次枚举到一个i的时候我们把所有$i$的倍数的点连向一个钦定的点(这个点就设为第一个$i$的倍数的点),这样我们就只需要枚举一个$k$就好了,不管是哪个合并到哪个,贡献都会是$i$

    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    
    const int N=1e5+15;
    int n,mx,cnt,pnt;
    ll ans;
    int a[N],vis[N],fa[N];
    inline int read(){
        char ch=getchar();int s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void chkmax(int &x,int y) {if (y>x) x=y;}
    int find(int x) {if (fa[x]!=x) fa[x]=find(fa[x]);return fa[x];}
    int main()
    {
        freopen("gcd.in","r",stdin);
        freopen("gcd.out","w",stdout);
        n=read();
        for (int i=1,x;i<=n;i++) {
            x=read();if (vis[x]) {ans+=x;continue;}
            vis[x]=1;fa[x]=x;
            a[++cnt]=x;chkmax(mx,x);
        }
        for (int i=mx;i&&pnt<cnt-1;i--)
        {
            int x=0,y;    
            for (int k=1,p;k*i<=mx&&pnt<cnt-1;k++){
                if (!vis[p=k*i]) continue;
                y=find(p);
                if (!x) x=y;else if (y!=x) fa[y]=x,ans+=i,++pnt;
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    UVa 1331 最大面积最小的三角剖分
    UVa 1626 括号序列(矩阵连乘)
    POJ 3295 Tautology(构造法)
    POJ 2586 Y2K Accounting Bug(贪心)
    POJ 2109 Power of Cryptography
    abcd
    好数
    Gift
    密码游戏
    约瑟夫游戏
  • 原文地址:https://www.cnblogs.com/xxzh/p/9804528.html
Copyright © 2020-2023  润新知