hiho 光棍節

描述

儘管付出了種種努力,jzp仍是得過光棍節。git

jzp很是不爽,但也無能爲力,只可以哀嘆起來他的命運。他想到了一位長者的人生經驗:「人的一輩子,不光要靠自我奮鬥,也要考慮歷史的進程」。ide

他終於明白本身只是時運不濟,因此又繼續開始努力。終於在聖誕節當天把到了妹子。spa

jzp今後過上了快樂的現充生活,在聖誕節當天,他還和妹子玩起了有趣的遊戲:code

jzp的家裏有一棵很是大的聖誕樹,能夠當作是一個n個點的無向聯通無環圖。每一個點上都有一個正整數,JZP和妹子每次都會選擇樹上的一條路徑,blog

這條路徑的權值被定義爲路徑上全部點上數的最大公約數,JZP能夠獲得這個權值的分數。遊戲

JZP玩了一下子有點膩了,他想知道對於每種可能的權值x,權值爲x的不一樣路徑的數量(a到b的路徑和b到a的路徑算做一種,a到a自身也算做一條路徑。)進程

輸入

第一行一個整數n(1<=n<=105)表示聖誕樹的大小,點從1開始標號。get

接下來一行n個整數用單個空格隔開,第i個表示第i個點上的數。(數都在1到105之間)。string

接下來n-1行,每行兩個數a和b,表示a和b之間有一條邊。it

輸出

令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\n",i,ans[i]);
    return 0;
}
View Code
相關文章
相關標籤/搜索