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

Input

31 2 31 22 3

Output

1 42 13 1

Input

61 2 4 8 16 321 66 33 44 26 5

Output

1 62 54 68 116 232 1

Input

49 16 144 61 32 34 3

Output

1 12 13 16 29 216 2144 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;
}

原文地址:https://www.cnblogs.com/hua-dong/p/9184355.html

时间: 2024-08-29 17:53:29

CodeForces990G:GCD Counting(树分治+GCD)的相关文章

CF1101D GCD Counting 点分治+质因数分解

想到本质不同质因数不会很多就切了~ Code: #include <cstdio> #include <vector> #include <algorithm> #define N 200004 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,tot,edges,sn,root,tl,answer; vector<int>v[N

CF1101D GCD Counting

CF1101D GCD Counting 又被trick了 不用什么点分治 直接树形dp即可 开始的想法: f[x][j]x为根的子树gcd至少为j(j是x的一个约数)的最长链 然后对y合并.类似于树的直径 但是复杂度还是很大的... 这个题的关键是:我们只关心gcd是不是1,并不关心gcd是什么! gcd不是1,意味着一定有公共质因子! 而质因子个数非常少 可以f[x][j]表示,x为根的子树,往下走,公共质因子为j的最长链 然后甚至可以暴力合并! 显然最优解可以被处理到! 代码: #incl

CF EDU 1101D GCD Counting 树形DP + 质因子分解

CF EDU 1101D GCD Counting 题意 有一颗树,每个节点有一个值,问树上最长链的长度,要求链上的每个节点的GCD值大于1. 思路 由于每个数的质因子很少,题目的数据200000<2*3*5*7*11*13*17=510510.所以每个节点的质因子个数不多.那么树形DP的时候直接枚举每种因子即可. //#pragma GCC optimize(3) //#pragma comment(linker, "/STACK:102400000,102400000") /

hdu 5869 区间不同GCD个数(树状数组)

Different GCD Subarray Query Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 221    Accepted Submission(s): 58 Problem Description This is a simple problem. The teacher gives Bob a list of probl

【BZOJ-1468】Tree 树分治

1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1025  Solved: 534[Submit][Status][Discuss] Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k Output 一行,有多少对点之间的距离小于等于k Sample Input 7 1 6 13 6

bzoj2152 树分治

还是太菜了,自己写的wa,但是找不到哪里错了,, 感觉现在学树分治早了点..以后回来再看吧 /* 多少点对之间的路径是3的倍数 */ #include<iostream> #include<cstring> #include<cstdio> using namespace std; #include<algorithm> #define MAXN 20010 int N; struct E{ int v,next,w; }edge[MAXN<<1

poj 1744 tree 树分治

Tree Time Limit: 1000MS   Memory Limit: 30000K       Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of ve

COJ 0970 WZJ的数据结构(负三十)树分治

WZJ的数据结构(负三十) 难度级别:D: 运行时间限制:1000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 给你一棵N个点的无根树,点和边上均有权值.请你设计一个数据结构,回答M次操作. 1 x v:对于树上的每一个节点y,如果将x.y在树上的距离记为d,那么将y节点的权值加上d*v. 2 x:询问节点x的权值. 输入 第一行为一个正整数N.第二行到第N行每行三个正整数ui,vi,wi.表示一条树边从ui到vi,距离为wi.第N+1行为一个正整数M.最后

Codeforces 321C Ciel the Commander 树分治裸题

题目链接 题意: 给定一棵树,要用字母A-Z 填到每个节点上 字母可以无限使用,但A至多只能用一次 目标:对于任意两个相同字母的节点,他们之间的路径上必须有至少一个节点的字母比他们小 例如:在两个C之间至少要有一个A 或者一个B 问: 输出填涂方案. 树分治即可,最多支持2^25个节点,不会无解. #include <iostream> #include <string> #include <vector> #include <cstring> #inclu