Live2d Test Env

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 21052⋅105 you have to count the number of pairs (x,y)(x,y) (1xyn)(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 (1n2105)(1≤n≤2⋅105).

The second line contains nn integers a1a1, a2a2, ..., anan (1ai2105)(1≤ai≤2⋅105) — the numbers written on vertices.

Then n1n−1 lines follow, each containing two integers xx and y(1x,yn,xy)(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 21052⋅105 do the following: if there is no pair (x,y)(x,y) such that xyx≤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

Input
3
1 2 3
1 2
2 3
Output
1 4
2 1
3 1
Input
6
1 2 4 8 16 32
1 6
6 3
3 4
4 2
6 5
Output
1 6
2 5
4 6
8 1
16 2
32 1
Input
4
9 16 144 6
1 3
2 3
4 3
Output
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\n",i,ans[i]);
    return 0;
}

 

posted @ 2018-06-14 18:07  nimphy  阅读(835)  评论(0编辑  收藏  举报