zoukankan      html  css  js  c++  java
  • 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
    ",i,ans[i]);
        return 0;
    }
  • 相关阅读:
    准备 LVM Volume Provider
    掌握 cinder-scheduler 调度逻辑
    Cinder 组件详解
    掌握 Cinder 的设计思想
    理解 Cinder 架构
    1 张图秒懂 Nova 16 种操作
    计算节点宕机了怎么办?- 每天5分钟玩转 OpenStack(43)
    Live Migrate 操作
    Resize Instance 操作详解
    关于Linux的10个核心面试问题与答案
  • 原文地址:https://www.cnblogs.com/hua-dong/p/9184355.html
Copyright © 2011-2022 走看看