题意
给定一棵
n
n
n 个节点的树,每个节点上有一个点权,边权为均
1
1
1,求所有点权互质的点对路径总和。
1
≤
n
,
点权
≤
100000
1 \leq n,\text{点权} \leq 100000
1≤n,点权≤100000
思路
对于一个数
a
a
a,能与它产生贡献的数和
a
a
a 没有公共质因子,那我们可以通过筛出莫比乌斯系数,就可以用容斥的方法求出与
a
a
a 互质的部分了,如下图:
A
,
B
,
C
A,B,C
A,B,C 为
a
a
a 的三个质因子,与
a
a
a 互质的部分就是上面的
U
∖
(
A
⋃
B
⋃
C
)
U\setminus (A\bigcup B\bigcup C)
U∖(A⋃B⋃C)
莫比乌斯系数筛法如下:
FOR(i,1,N)mo[i]=1,mark[i]=0;
FOR(i,2,N)if(!mark[i])
{
for(int j=i;j<=N;j+=i)
{
mark[j]=1;
if(j/i%i==0)mo[j]=0;
else mo[j]=-mo[j];
}
}
其中
μ
(
x
)
=
{
1
if n is a square-free positive integer with an even number of prime factors
−
1
if n is a square-free positive integer with an odd number of prime factors
0
if n has a squared prime factor.
\mu(x){}=\begin{cases} \ \ \ 1\quad \text{if n is a square-free positive integer with an even number of prime factors}\\ -1\quad \text{if n is a square-free positive integer with an odd number of prime factors}\\ \ \ \ 0\quad \text{if n has a squared prime factor.} \end{cases}
μ(x)=⎩⎪⎨⎪⎧ 1if n is a square-free positive integer with an even number of prime factors−1if n is a square-free positive integer with an odd number of prime factors 0if n has a squared prime factor.
若一个数有一个因子是某个质数平方,那这个集合是某一个质数代表集合的子集,不会有贡献计算。即一个数表示成若干个质数的几次幂相乘时,这个幂次只能是
1
1
1。除此外,有偶数个质因子的莫比乌斯函数值是
1
1
1,奇数个为
−
1
-1
−1。
那我们不妨枚举
LCA
\text{LCA}
LCA,计算每一个父节点中符合条件的子节点对的贡献,单独对某个因子而言,两个同时是这个因子倍数的数将产生贡献,乘上莫比乌斯系数再累和就是所有的贡献。
贡献的算法只用化开式子即可,对于因子
s
s
s,某一个节点
u
u
u,给定所有节点的
l
c
a
lca
lca 时,对答案的贡献就是
m
o
s
∑
v
Dis
(
v
,
u
)
mo_s\displaystyle\sum_v\text{Dis}(v,u)
mosv∑Dis(v,u) ,其中
u
,
v
u,v
u,v 属于
l
c
a
lca
lca 不同子节点的子树。
然后我们有
m
o
s
∑
v
(
d
e
p
u
+
d
e
p
v
−
2
d
e
p
l
c
a
)
mo_s\displaystyle\sum_v(dep_u+dep_v-2dep_{lca})
mosv∑(depu+depv−2deplca)
设
s
u
m
s
sum_s
sums 为目前全局数组中
s
s
s 倍数的节点深度总和,
c
n
t
s
cnt_s
cnts 为个数总和。
m
o
s
(
s
u
m
v
+
c
n
t
s
d
e
p
u
−
2
c
n
t
s
d
e
p
l
c
a
)
mo_s(sum_v+cnt_sdep_u-2cnt_sdep_{lca})
mos(sumv+cntsdepu−2cntsdeplca)
维护好
s
u
m
sum
sum 和
c
n
t
cnt
cnt 数组,每添加一棵子树就计算一次贡献即可。
把它改成
d
s
u
dsu
dsu 复杂度就变成
O
(
n
log
n
)
O(n\log n)
O(nlogn) 了。
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=1e5+3;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
Linked_list(){clear();}
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
Linked_list<N,N*64>fac;
int mo[N];bool mark[N];
int L[N],R[N],ori[N],ord,dep[N],fa[N],sz[N],son[N],a[N],n;
LL cnt[N],sum[N];
LL ans;
void init()
{
FOR(i,1,N-3)mo[i]=1,mark[i]=0;
FOR(i,2,N-3)if(!mark[i])
{
for(int j=i;j<=N-3;j+=i)
{
mark[j]=1;
if(j/i%i==0)mo[j]=0;
else mo[j]=-mo[j];
}
}
FOR(i,1,N-3)if(mo[i])
for(int j=i;j<=N-3;j+=i)
fac.add(j,i);
return;
}
void dfs(int u,int f,int d)
{
dep[u]=d,fa[u]=f,sz[u]=1,son[u]=0;
L[u]=++ord,ori[ord]=u;
EOR(i,G,u)
{
int v=G.to[i];
if(v==f)continue;
dfs(v,u,d+1);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
R[u]=ord;
}
void update(int l,int r,int k)
{
FOR(i,l,r)
{
int u=ori[i];
EOR(i,fac,a[u])
{
int s=fac.to[i];
cnt[s]+=k;
sum[s]+=k*dep[u];
}
}
}
void calc(int l,int r,int lca)
{
FOR(i,l,r)
{
int u=ori[i];
EOR(i,fac,a[u])
{
int s=fac.to[i];
ans+=mo[s]*(sum[s]+cnt[s]*dep[u]-2*dep[lca]*cnt[s]);
}
}
}
void dsu(int u,int f)
{
EOR(i,G,u)
{
int v=G.to[i];
if(v==f||v==son[u])continue;
dsu(v,u);update(L[v],R[v],-1);
}
if(son[u])dsu(son[u],u);
EOR(i,G,u)
{
int v=G.to[i];
if(v==f||v==son[u])continue;
calc(L[v],R[v],u),update(L[v],R[v],1);
}
calc(L[u],L[u],u),update(L[u],L[u],1);
}
int main()
{
init();
scanf("%d",&n);
FOR(i,1,n)scanf("%d",&a[i]);
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
G.add(u,v),G.add(v,u);
}
dfs(1,0,1);
dsu(1,0);
printf("%lld\n",ans);
return 0;
}