zoukankan      html  css  js  c++  java
  • hiho 光棍节

    描述

    尽管付出了种种努力,jzp还是得过光棍节。

    jzp非常不爽,但也无能为力,只能够哀叹起来他的命运。他想到了一位长者的人生经验:“人的一生,不光要靠自我奋斗,也要考虑历史的进程”。

    他终于明白自己只是时运不济,所以又继续开始努力。终于在圣诞节当天把到了妹子。

    jzp从此过上了快乐的现充生活,在圣诞节当天,他还和妹子玩起了有趣的游戏:

    jzp的家里有一棵非常大的圣诞树,可以看成是一个n个点的无向联通无环图。每个点上都有一个正整数,JZP和妹子每次都会选择树上的一条路径,

    这条路径的权值被定义为路径上所有点上数的最大公约数,JZP可以得到这个权值的分数。

    JZP玩了一会儿有点腻了,他想知道对于每种可能的权值x,权值为x的不同路径的数量(a到b的路径和b到a的路径算作一种,a到a自身也算作一条路径。)

    输入

    第一行一个整数n(1<=n<=105)表示圣诞树的大小,点从1开始标号。

    接下来一行n个整数用单个空格隔开,第i个表示第i个点上的数。(数都在1到105之间)。

    接下来n-1行,每行两个数a和b,表示a和b之间有一条边。

    输出

    令C(x)表示权值为x的路径的个数。

    从小到大输出对于所有C(x)>0的x,输出一行两个数x和C(x),用空格隔开。

    样例输入

    20
    2 4 2 4 2 4 2 20 20 12 12 12 2 12 2 4 4 2 12 2
    1 2
    1 3
    1 4
    2 5
    3 6
    1 7
    6 8
    2 9
    6 10
    5 11
    4 12
    11 13
    10 14
    3 15
    9 16
    7 17
    4 18
    4 19
    16 20

    样例输出

    2 186
    4 16
    12 6
    20 2


    首先考虑将问题转化成计算有多少路径的gcd是k的倍数,然后容斥计算出答案。
    注意一条路径的gcd是k的倍数等同于路径每条边gcd的gcd,那么我们预处理出对于每个k有那些边符合要求,就可以计算了。
    枚举k,将符合条件的边建出来,可以用个并查集,在合并时顺便计算答案。
    需要预处理出每个k的因数,暴力分解会T。
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=Next[i])
    using namespace std;
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    typedef long long ll;
    const int maxn=100010;
    int first2[maxn],Next2[maxn*20],id2[maxn*20],tot2;
    void AddP(int p,int v) {id2[++tot2]=v;Next2[tot2]=first2[p];first2[p]=tot2;}
    int first[maxn],Next[maxn*20],id[maxn*20],tot;
    void AddN(int p,int v) {id[++tot]=v;Next[tot]=first[p];first[p]=tot;}
    int n,A[maxn],u[maxn],v[maxn],pa[maxn],sz[maxn],S[maxn<<1];
    int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
    int findset(int x) {return pa[x]==x?x:findset(pa[x]);}
    ll ans[maxn],res;
    void merge(int a,int b) {
        int x=findset(a),y=findset(b);
        if(x==y) return;
        res+=(ll)sz[x]*sz[y];
        sz[x]+=sz[y];pa[y]=x;
    }
    int main() {
        n=read();
        rep(i,1,n) A[i]=read(),pa[i]=i,sz[i]=1;
        rep(i,1,100000) for(int j=i;j<=100000;j+=i) AddP(j,i);
        rep(i,1,n-1) {
            u[i]=read();v[i]=read();
            int x=gcd(A[u[i]],A[v[i]]);
            for(int j=first2[x];j;j=Next2[j]) AddN(id2[j],i);
        }
        dwn(x,100000,1) {
            res=0;int cnt=0;
            ren {
                merge(u[id[i]],v[id[i]]);
                S[++cnt]=u[id[i]];S[++cnt]=v[id[i]];
            }
            rep(i,1,cnt) pa[S[i]]=S[i],sz[S[i]]=1;
            ans[x]=res;
            for(int j=2*x;j<=100000;j+=x) ans[x]-=ans[j];
        }
        rep(i,1,n) ans[A[i]]++;
        rep(i,1,100000) if(ans[i]) printf("%d %lld
    ",i,ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    奇异值分解(SVD)与在降维中的应用
    拉格朗日乘子法&KKT条件
    有监督学习、无监督学习、半监督学习
    LSTM学习—Long Short Term Memory networks
    Ubuntu16.04+CUDA8.0+cuDNN5.1+Python2.7+TensorFlow1.2.0环境搭建
    激活函数
    C基础学习笔记
    Hive
    Fragment跳转至Activity片段随笔
    冒泡排序和选择排序
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/4760223.html
Copyright © 2011-2022 走看看