1. 程式人生 > >cf 990G GCD Counting (莫比烏斯反演 並查集)

cf 990G GCD Counting (莫比烏斯反演 並查集)

In http 自己 har urn merge cto count read

990G

給你一棵樹,問你有多少個點對(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; }

cf 990G GCD Counting (莫比烏斯反演 並查集)