题目很简单,但是想要写出比较优的复杂度需要基础比较扎实。
给定一棵 (n) 个结点的有根树,点 (u) 有点权 (a_u)。求每个点 (u) 的子树内有多少点 (v) 满足 (a_u) 和 (a_v) 互质。
(1le nle 10^5,1le a_ule 10^5),多组数据,数据组数不超过 (8)
即求 (sum_{v in subtree u} [gcd(a_u,a_v)=1])。
莫比乌斯反演得 (sum_{d|a_u}mu(d)sum_{v in subtree u} [d|a_v])。
也就是对 (a_u) 的每个约数 (d) 求子树里有多少 (d) 的倍数。
DFS 序上树状数组差不多是 (O(max a_i+sum d(a_i)log n)) 的,可能就可以过了。
我们采用一个经典套路:DFS 一遍树,对 (d) 开一个桶 (b[]),经过点 (u) 时把其所有约数塞进桶里。那么答案等于从 (u) 的子树出去时的 (sum_{d|a_u} mu(d)b[d]) 减去进来时的 (sum_{d|a_u} mu(d)b[d]) 。复杂度降到 (O(max a_i+sum d(a_i)))。
然后注意到只有分解质因数形如 (x=p_1p_2cdots p_k) 的数满足 (mu(x) e 0),而 (a) 的这样的约数只有 (2^{omega(a)}) 个。复杂度 (O(max a_i+sum 2^{omega(a_i)}))。
最后回顾一个质因数分解的好办法:线筛出每个数的最小质因数,然后一直除即可。
#include<bits/stdc++.h>
const int N=1e5+3,A=1e5+3,K=10;
std::vector<int>g[N];
int a[N],n,p[A],k,d[A],mu[A],s[N],t[A];
bool np[A];
void Dfs(int u,int fa){
int v,b,q[K],c;
c=0;
for(b=a[u];b>1;b/=d[b])if(!c||q[c-1]!=d[b])q[c++]=d[b];
for(int I=0;I<(1<<c);I++){
b=1;for(int j=0;j<c;j++)if(I>>j&1)b*=q[j];
s[u]-=mu[b]*t[b];
++t[b];
}
for(int i=0;i<g[u].size();i++)if((v=g[u][i])!=fa)Dfs(v,u);
for(int I=0;I<(1<<c);I++){
b=1;for(int j=0;j<c;j++)if(I>>j&1)b*=q[j];
s[u]+=mu[b]*t[b];
}
}
int main(){
int u,v;
mu[1]=1;
for(int i=2;i<A;i++){
if(!np[i])p[++k]=i,mu[i]=-1,d[i]=i;
for(int j=1;j<=k&&i*p[j]<A;j++){
np[i*p[j]]=1;
if(i%p[j])mu[i*p[j]]=-mu[i],d[i*p[j]]=p[j];
else{d[i*p[j]]=d[i];break;}
}
}
for(int T=1;~scanf("%d",&n);T++){
for(int i=0;i<A;i++)t[i]=0;
for(u=1;u<=n;u++)g[u].clear(),s[u]=0;
for(int i=1;i<n;i++)scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
for(u=1;u<=n;u++)scanf("%d",a+u);
Dfs(1,0);
printf("Case #%d:",T);for(u=1;u<=n;u++)printf(" %d",s[u]);puts("");
}return 0;
}