儘管付出了種種努力,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; }