题意:
解法:
点分治.
对于点x, 用map维护子树点到x的数的个数,
每次暴力计算经过x的路径即可,
由于gcd是收敛的, 数不会太多.
code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxm= 2e5 + 5 ;
int head[ maxm] , nt[ maxm<< 1 ] , to[ maxm<< 1 ] , tot;
int sz[ maxm] , son[ maxm] , root, size;
int mark[ maxm] ;
ll cnt[ maxm] ;
int a[ maxm] ;
int n;
void add ( int x, int y) {
tot++ ; nt[ tot] = head[ x] ; head[ x] = tot; to[ tot] = y;
}
void getroot ( int x, int fa) {
sz[ x] = 1 ; son[ x] = 0 ;
for ( int i= head[ x] ; i; i= nt[ i] ) {
int v= to[ i] ;
if ( v== fa|| mark[ v] ) continue ;
getroot ( v, x) ;
sz[ x] + = sz[ v] ;
son[ x] = max ( son[ x] , sz[ v] ) ;
}
son[ x] = max ( son[ x] , size- sz[ x] ) ;
if ( son[ x] < son[ root] ) root= x;
}
map< int , int > last;
map< int , int > now;
void dfs ( int x, int fa, int val) {
now[ val] ++ ;
for ( int i= head[ x] ; i; i= nt[ i] ) {
int v= to[ i] ;
if ( v== fa|| mark[ v] ) continue ;
dfs ( v, x, __gcd ( val, a[ v] ) ) ;
}
}
void solve ( int x) {
cnt[ a[ x] ] ++ ;
last. clear ( ) ;
last[ a[ x] ] ++ ;
for ( int i= head[ x] ; i; i= nt[ i] ) {
int v= to[ i] ;
if ( mark[ v] ) continue ;
now. clear ( ) ;
dfs ( v, x, __gcd ( a[ x] , a[ v] ) ) ;
for ( auto i: now) {
for ( auto j: last) {
cnt[ __gcd ( i. first, j. first) ] + = 1ll * i. second* j. second;
}
}
for ( auto i: now) {
last[ i. first] + = i. second;
}
}
}
void divide ( int x) {
mark[ x] = 1 ;
solve ( x) ;
for ( int i= head[ x] ; i; i= nt[ i] ) {
int v= to[ i] ;
if ( mark[ v] ) continue ;
son[ root= 0 ] = size= sz[ v] ;
getroot ( v, v) ;
divide ( root) ;
}
}
void solve ( ) {
scanf ( "%d" , & n) ;
for ( int i= 1 ; i<= n; i++ ) scanf ( "%d" , & a[ i] ) ;
for ( int i= 1 ; i< n; i++ ) {
int a, b; scanf ( "%d%d" , & a, & b) ;
add ( a, b) ; add ( b, a) ;
}
son[ root= 0 ] = size= n;
getroot ( 1 , 1 ) ;
divide ( root) ;
for ( int i= 1 ; i<= 2e5 ; i++ ) {
if ( cnt[ i] ) {
printf ( "%d %lld\n" , i, cnt[ i] ) ;
}
}
}
signed main ( ) {
solve ( ) ;
return 0 ;
}