给你一棵树,问你有多少个点对(x,y)(x\(\leq\)y),使得(x,y)简单路径上的点权值的\(gcd\)为\(i\),对于\(i\in [1,200000]\)输出点对数目。
这题没有做出来,主要还是莫比乌斯反演时间太长不熟悉了。同时统计点对的技巧也自己没有想出来,实在是不应该。
我们设\(h(i)\)是有多少个点对(x,y)(x\(\leq\)y),使得(x,y)简单路径上的点权值的\(gcd\)为\(i\)的倍数的答案。于是有\(h(i)=\sum_{i=1}^{\lfloor maxa/i \rfloor} ans(k\cdot i)\)。即\(ans(i)=\sum_{k=1}^{\lfloor maxa/i \rfloor} h(k\cdot i)\mu(k)\)。
于是我们就要统计\(h(i)\)。这个怎么统计呢,就是要有这样的简单路径存在那肯定是有一整个联通块,这个联通块里的点的点权全部是\(i\)的倍数,我们就要找这些联通块出来。把所有\(i\)的倍数的点拿出来,并查集合并大小到父亲,然后统计所有的父亲对答案的贡献即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int readInt() {
char c; int tmp=0,x=1; c=getchar();
while(c>‘9‘ || c<‘0‘) {if(c==‘-‘) x=-1; c=getchar();}
while(c>=‘0‘ && c<=‘9‘) {tmp=tmp*10+c-‘0‘; c=getchar();}
return tmp*x;
}
const int maxN=200000+10;
int a[maxN],n;
vector<int > g[maxN];
void addEdge(int u,int v) {
g[u].push_back(v); g[v].push_back(u);
}
bool vis[maxN];
int pri[maxN],tot=0,mu[maxN];
void sieve() {
mu[1]=1;
for(int i=2;i<maxN;i++) {
if(!vis[i]) pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot && pri[j]*i<maxN;j++) {
vis[pri[j]*i]=true;
if(i%pri[j]==0) {
mu[pri[j]*i]=0;
break;
}
mu[pri[j]*i]=-mu[i];
}
}
}
int Fa[maxN];
void dfs(int v,int fa) {
Fa[v]=fa;
for(int i=0;i<(int)g[v].size();i++) {
int u=g[v][i];
if(u!=fa) dfs(u,v);
}
}
int siz[maxN],f[maxN];
int getFather(int x) {
return x==f[x]?f[x]:f[x]=getFather(f[x]);
}
void mergeUnion(int u,int v) {
int fu=getFather(u),fv=getFather(v);
if(fu==fv) return;
else {
if(siz[fu]>siz[fv]) swap(fu,fv);
siz[fv]+=siz[fu];
f[fu]=fv;
}
}
vector<int > hav[maxN],all;
ll h[maxN],ans[maxN];
int main() {
sieve();
memset(vis,0,sizeof(vis));
n=readInt();
for(int i=1;i<=n;i++) a[i]=readInt(),hav[a[i]].push_back(i);
int u,v;
for(int i=1;i<=n-1;i++) {
u=readInt(),v=readInt();
addEdge(u,v);
}
dfs(1,-1);
for(int i=1;i<=200000;i++) {
for(int k=0;k<(int)all.size();k++) siz[all[k]]=0,f[all[k]]=0,vis[all[k]]=false;
all.clear();
for(int j=i;j<=200000;j+=i) {
for(int k=0;k<(int)hav[j].size();k++) {
siz[hav[j][k]]=1,f[hav[j][k]]=hav[j][k];
all.push_back(hav[j][k]);
}
}
for(int k=0;k<(int)all.size();k++) {
v=all[k];
if(Fa[v]!=-1 && a[Fa[v]]%i==0) mergeUnion(Fa[v],v);
}
for(int k=0;k<(int)all.size();k++) {
v=all[k];
if(!vis[u=getFather(v)]) h[i]+=1ll*siz[u]*(siz[u]+1)/2,vis[u]=true;
}
}
for(int i=1;i<=200000;i++) {
for(int j=1;j<=(200000/i);j++) {
ans[i]+=h[j*i]*1ll*mu[j];
}
}
for(int i=1;i<=200000;i++) {
if(ans[i]>0) printf("%d %I64d\n",i,ans[i]);
}
return 0;
}
原文地址:https://www.cnblogs.com/darkroome/p/9201674.html
时间: 2024-10-29 03:12:10