CF990G 题解

CF990G 题解

$\text{Description}$

给定一棵点带权无根树,对于每个 $k\in[1,2\cdot 10^5]$,求出有多少个无序点对 $(x,y)$ 满足 $x$ 到 $y$ 的简单路径上的所有节点的点权的 $\gcd$ 为 $k$。

$1\le n,a_i\le 2\cdot 10^5$

$\text{Solution}$

为了方便,下设点权值域为 $A$。

直接统计 $\gcd$ 不大方便,考虑莫比乌斯反演一下。

记 $F(x)$ 表示当前有多少链的 $\gcd$ 为 $x$,$G(x)$ 表示当前有多少链的 $\gcd$ 包含 $x$ 这一因子,则有

如果能计算出 $G(x)$,则我们可以 $\mathcal O(A\log A)$ 地计算出 $F(x)$。

考虑一条链的 $\gcd$ 包含 $x$ 这一因子,意味着该链上所有点的点权都包含 $x$ 这一因子。

对于每一个 $x$,我们将所有点权不包含 $x$ 这一因子的点及其连出的边删去,$G(x)$ 即剩余各个连通块内的链个数。

暴力计算是 $\mathcal O(nA)$ 的,无法通过。注意到一个数的因子个数是 $\mathcal O(\sqrt A)$ 级别的,我们考虑开个 vector,vector 的第 $x$ 位存所有点权包含 $x$ 这一因子的点。当我们在计算 $x$ 的时候,仅枚举其对应的 vector 中的点与两端点都在该 vector 内的边即可。

时间复杂度 $\mathcal O(n\sqrt A)$,可以通过本题。

$\text{Code}$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<bits/stdc++.h>
#define REG register
using namespace std;
inline void read(int& x){
static char c;
while(!isdigit(c=getchar()));x=c^48;
while(isdigit(c=getchar()))x=(x*10)+(c^48);
}

const int N=200005;

int n;

int A[N],mu[N],Prm[N],vis[N],Pcnt;

inline void Euler(){
mu[1]=1;
for(REG int i=2;i<=N-5;++i){
if(!vis[i]) Prm[++Pcnt]=i,mu[i]=-1;
for(REG int j=1;j<=Pcnt&&i*Prm[j]<=N-5;++j){
vis[i*Prm[j]]=1;
if(i%Prm[j]==0) break;
mu[i*Prm[j]]=-mu[i];
}
}
}

long long F[N],G[N];

struct Edge{int v,nxt;}ed[N<<1];
int head[N],cnt;
inline void adde(int u,int v){ed[++cnt]=(Edge){v,head[u]},head[u]=cnt;}
inline void add(int u,int v){adde(u,v),adde(v,u);}

vector<int> V[N];

int gcd(int a,int b){return b?gcd(b,a%b):a;}

int tot,Vis[N];

void dfs(int now,int fa,int d){
++tot,Vis[now]=1;
for(REG int i=head[now];i;i=ed[i].nxt){
int v=ed[i].v;
if(v==fa||A[v]%d) continue;
dfs(v,now,d);
}
}

inline void Work(){
Euler();
read(n);
for(REG int i=1;i<=n;++i) read(A[i]);
for(REG int i=1;i<=n;++i)
for(REG int j=1;j*j<=A[i];++j){
if(A[i]%j) continue;
V[j].push_back(i);
if(j^(A[i]/j)) V[A[i]/j].push_back(i);
}
for(REG int i=1;i<n;++i){
int u,v;read(u),read(v),add(u,v);
}
for(REG int i=1;i<=N-5;++i){
for(REG int j=0;j<V[i].size();++j)
if(!Vis[V[i][j]]) tot=0,dfs(V[i][j],0,i),G[i]+=1ll*tot*(tot+1)/2;
for(REG int j=0;j<V[i].size();++j)
Vis[V[i][j]]=0;
}
for(REG int i=1;i<=N-5;++i)
for(REG int j=i;j<=N-5;j+=i)
F[i]+=G[j]*mu[j/i];
for(REG int i=1;i<=N-5;++i)
if(F[i]) printf("%d %lld\n",i,F[i]);
}

int main(){Work();}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×