题目很简单,但是想要写出比较优的复杂度需要基础比较扎实。
给定一棵 \(n\) 个结点的有根树,点 \(u\) 有点权 \(a_u\)。求每个点 \(u\) 的子树内有多少点 \(v\) 满足 \(a_u\) 和 \(a_v\) 互质。
\(1\le n\le 10^5,1\le a_u\le 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_2\cdots p_k\) 的数满足 \(\mu(x)\ne 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;
}