CodeForces990G:GCD Counting(树分治+GCD)
Posted hua-dong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeForces990G:GCD Counting(树分治+GCD)相关的知识,希望对你有一定的参考价值。
You are given a tree consisting of nn vertices. A number is written on each vertex; the number on vertex ii is equal to aiai.
Let‘s denote the function g(x,y)g(x,y) as the greatest common divisor of the numbers written on the vertices belonging to the simple path from vertex xx to vertex yy(including these two vertices).
For every integer from 11 to 2⋅1052⋅105 you have to count the number of pairs (x,y)(x,y) (1≤x≤y≤n)(1≤x≤y≤n) such that g(x,y)g(x,y) is equal to this number.
Input
The first line contains one integer nn — the number of vertices (1≤n≤2⋅105)(1≤n≤2⋅105).
The second line contains nn integers a1a1, a2a2, ..., anan (1≤ai≤2⋅105)(1≤ai≤2⋅105) — the numbers written on vertices.
Then n−1n−1 lines follow, each containing two integers xx and yy (1≤x,y≤n,x≠y)(1≤x,y≤n,x≠y)denoting an edge connecting vertex xx with vertex yy. It is guaranteed that these edges form a tree.
Output
For every integer ii from 11 to 2⋅1052⋅105 do the following: if there is no pair (x,y)(x,y) such that x≤yx≤y and g(x,y)=ig(x,y)=i, don‘t output anything. Otherwise output two integers: iiand the number of aforementioned pairs. You have to consider the values of ii in ascending order.
See the examples for better understanding.
Examples
3
1 2 3
1 2
2 3
1 4
2 1
3 1
6
1 2 4 8 16 32
1 6
6 3
3 4
4 2
6 5
1 6
2 5
4 6
8 1
16 2
32 1
4
9 16 144 6
1 3
2 3
4 3
1 1
2 1
3 1
6 2
9 2
16 2
144 1
题意:求所有简单路径的GCD,统计数量。
思路:不难想到是分治,问题转化为多个小问题:统计经过某点的路径的GCD,由于GCD具有收敛性,不同GCD的数量级是log级别的,虽然有多个链,但感觉gcd是数量就算不会太多,2333,我猜复杂度不超过O(N*logN*logN*logN)级别吧。所以对于当前子树,每次访问一条链的时候统计这条链和之前所有GCD的gcd。。。。说不清楚,反正一想就会相通的东西。
(具有收敛性的有:GCD,或,且...)
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=200010; const int inf=0x7FFFFFFF; int Laxt[maxn],Next[maxn<<1],To[maxn<<1],cnt,N,sn; int a[maxn],sz[maxn],son[maxn],vis[maxn],root; ll ans[maxn]; map<int,int>mp,tp; map<int,int>::iterator it1,it2; inline void read(int &x) { x=0; char c=getchar(); while(c>‘9‘||c<‘0‘) c=getchar(); while(c<=‘9‘&&c>=‘0‘) x=(x<<3)+(x<<1)+c-‘0‘,c=getchar(); } void add(int u,int v){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void getroot(int u,int fa) //找重心 { sz[u]=1; son[u]=0; for(int i=Laxt[u];i;i=Next[i]){ if(To[i]!=fa&&!vis[To[i]]){ getroot(To[i],u); sz[u]+=sz[To[i]]; son[u]=max(son[u],sz[To[i]]); } } son[u]=max(son[u],sn-son[u]); if(root==0||son[root]>son[u]) root=u; } void getans(int u,int fa,int num) //对于当前链产生的新GCD { tp[num]++; for(int i=Laxt[u];i;i=Next[i]){ if(!vis[To[i]]&&To[i]!=fa){ getans(To[i],u,__gcd(num,a[To[i]])); } } } void solve(int u) //解决以u为根的子问题 { mp.clear(); mp[a[u]]++; ans[a[u]]++; for(int i=Laxt[u];i;i=Next[i]) if(!vis[To[i]]) { tp.clear(); getans(To[i],u,__gcd(a[u],a[To[i]])); for(it1=mp.begin();it1!=mp.end();it1++) for(it2=tp.begin();it2!=tp.end();it2++){ int g=__gcd((*it1).first,(*it2).first); ans[g]+=(ll)(*it1).second*(*it2).second; } for(it2=tp.begin();it2!=tp.end();it2++) mp[(*it2).first]+=(*it2).second; } } void dfs(int u) //分治 { vis[u]=1; solve(u); for(int i=Laxt[u];i;i=Next[i]){ if(vis[To[i]]) continue; root=0; sn=sz[To[i]]; getroot(To[i],0); dfs(root); } } int main() { read(N); int u,v,Max=0; for(int i=1;i<=N;i++) read(a[i]),Max=max(Max,a[i]); for(int i=1;i<N;i++) { read(u);read(v); add(u,v); add(v,u); } root=0; sn=N; getroot(1,0); dfs(root); for(int i=1;i<=Max;i++) if(ans[i]) printf("%d %I64d ",i,ans[i]); return 0; }
以上是关于CodeForces990G:GCD Counting(树分治+GCD)的主要内容,如果未能解决你的问题,请参考以下文章
CF990G GCD Counting(树上莫比乌斯反演,分层图,并查集)
CodeForces-1152C Neko does Maths(GCD)