1. 程式人生 > >XSY原創題 友好國度

XSY原創題 友好國度

題目大意

給定一棵樹,每個點有點權,求有多少組點對滿足兩點簡單路徑上的所有點點權的$gcd=1$。

$n,val_i\leq 10^5$

 

題解

考慮設$G_i$表示簡單路徑上所有點點權均為$i$的倍數的點對數。

那麼最終答案顯然就是$\sum G_i \mu(i)$。

由於求$gcd$,那麼點權某一個質因子次數大於$2$是沒有意義的,所以$val$最多有$6$個質因子

$(2\times 3\times 5\times 7\times 11\times 13=30030)$。

那麼一個數的約數不超過$64$個,那麼開$n$個$vector$,$vector_i$存點權是$i$倍數的點的集合。

若求$G_i$,只需要求全部由$vector_i$中的點組成的路徑數即可。

那麼將這若干個點取出來,在原樹構成若干個連通塊,那麼每一個連通塊內任意兩點組成路徑均能貢獻,則答案$=\frac{n(n-1)}{2}$。

最後再求與莫比烏斯函式的點積之和即可。

 

#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<" = "<<x
#define sp <<"  "
#define el <<endl
#define LL long long
#define M 100020
using namespace std;
namespace IO{
	const int BS=(1<<20)+5; char Buffer[BS],*HD,*TL;
	char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;}
	int read(){
		int nm=0,fh=1; char cw=Getchar();
		for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh;
		for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0');
		return nm*fh;
	}
}using namespace IO;
int sz[M],n,m,fs[M],nt[M<<1],to[M<<1],val[M],tmp; vector<int>G[M];
int p[M],tot,mu[M],vis[M]; bool isp[M]; LL ans;
#define link(a,b) nt[tmp]=fs[a],fs[a]=tmp,to[tmp++]=b
void init(){
	memset(isp,true,sizeof(isp)),mu[1]=1,isp[1]=false;
	for(int i=2;i<M;i++){
		if(isp[i]) p[++tot]=i,mu[i]=-1;
		for(int j=1;p[j]*i<M&&j<=tot;j++){
			isp[p[j]*i]=false;if(i%p[j]==0) break;mu[p[j]*i]=-mu[i];
		}
	}
}
void dfs(int x,int last,int num){
	vis[x]=num,sz[x]=1;
	for(int i=fs[x];i!=-1;i=nt[i]){
		if(to[i]==last||val[to[i]]%num) continue;
		dfs(to[i],x,num),sz[x]+=sz[to[i]];
	}
}
void solve(int num,LL res=0){
	for(int k=0,TT=G[num].size();k<TT;k++){
		int x=G[num][k];
		if(vis[x]==num) continue;
		dfs(x,0,num),res+=((LL)sz[x]*(LL)(sz[x]-1))>>1;
	} if(res) ans+=mu[num]*res;
}
int main(){
	init(),n=read(),memset(fs,-1,sizeof(fs));
	for(int i=1;i<n;i++){int x=read(),y=read();link(x,y),link(y,x);}
	for(int i=1;i<=n;i++){
		val[i]=read(); 
		for(int j=1;p[j]*p[j]<=p[i];j++) while(val[i]%(p[j]*p[j])==0) val[i]/=p[j];
		for(int j=1;j*j<=val[i];j++){
			if(val[i]%j) continue; G[j].push_back(i);
			if(j*j<val[i]) G[val[i]/j].push_back(i);
		}
	}
	for(int i=1;i<M;i++) solve(i); printf("%lld\n",ans); return 0;
}