T2 路径(route)
(WOJ4821)
【题目描述】
有一棵
n
n
n个节点的无根树,树上第
i
i
i个节点有一个正整数
A
i
A_i
Ai 作为点权。有趣的是,这棵无根树度数为
1
1
1 的节点不超过
10
10
10 个。
请求出一条树上的路径,使得路径上包含的节点个数乘以路径经过点权的最大公约数最大。
【输入格式】
从文件route.in 中读入数据。
第一行为一个正整数
n
n
n,表示树的大小。第二行为
n
n
n 个正整数
A
i
A_i
Ai, 表示点的权值。
接下来
n
+
1
n+1
n+1 行,每行两个正整数
u
,
v
u,v
u,v,表示
u
u
u 和
v
v
v 在树上相连。
【输出格式】
输出到文件route.out 中。
输出一行一个正整数表示路径经过点权的最大公约数乘以节点个数的最大值。
【样例1 输入】
5
3 3 6 3 6
1 2
2 3
2 5
1 4
【样例1 输出】
12
【样例1 解释】
选择路径
(
3
,
4
)
(3 , 4)
(3,4) 或
(
5
,
4
)
(5 , 4)
(5,4),经过节点点权为
6
,
3
,
3
,
3
6,3,3,3
6,3,3,3。路径包含
4
4
4 个节点,最大公约数为
3
3
3,故答案为
12
12
12。
【样例2】
见选手目录下的route/route2.in 与route/route2.ans。
【子任务】
对于100% 的数据满足:
1
≤
n
≤
160
,
000
,
1
≤
u
i
,
v
i
≤
n
,
1
≤
A
i
≤
1
0
9
1\le n\le 160,000,1\le u_i,v_i\le n,1\le A_i\le 10^9
1≤n≤160,000,1≤ui,vi≤n,1≤Ai≤109。
思路:
处理链的情况,发现一个点
i
i
i到
j
(
i
<
j
)
j(i<j)
j(i<j)的路径
g
c
d
gcd
gcd,如果不为
v
a
l
[
i
]
val[i]
val[i],则小于等于
v
a
l
[
i
]
/
2
val[i]/2
val[i]/2,即
g
c
d
gcd
gcd一次变化至少除以二,那么
g
c
d
gcd
gcd至多变化
l
o
g
v
a
l
[
i
]
log\ val[i]
log val[i]次
(
<
30
)
(\lt 30)
(<30)。
对于每一个点用数组维护
g
c
d
gcd
gcd变化的位置和值,发现对于
i
i
i的数组可以从
i
−
1
i-1
i−1变化而来:
将
v
a
l
[
i
]
val[i]
val[i]加入数组,表示当前位置。每一个位置的值与
v
a
l
[
i
]
val[i]
val[i]求
g
c
d
gcd
gcd,然后去重。用每一个位置的
g
c
d
gcd
gcd与距离更新
a
n
s
ans
ans。
处理树:
发现叶节点数量小于
10
10
10,并且以每一个叶节点为根进行深搜时,图上的每一条链都在深搜维护节点数组时被包含。
证明:
每一条链向两边延伸后,链的两端必为叶节点。
所以每一个叶节点为根进行深搜,类似维护链的情况进行维护即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define LL long long
inline char ch(){
static char buf[1<<20],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++;
}
inline LL in{
LL s=0,f=1;char x;
for(x=ch();!isdigit(x);x=ch()) if(x=='-') f=-1;
for( ;isdigit(x);x=ch()) s=(s<<1)+(s<<3)+(x&15);
return s*f;
}
const LL A=4e5+5;
LL n;
LL val[A];
LL head[A],tot;
struct Road{
LL nex,to;
}road[2*A];
inline void ljb(LL x,LL y){
road[++tot].nex=head[x],road[tot].to=y,head[x]=tot;
}
LL rd[A],root[15],all;
inline LL find_gcd(LL x,LL y){
return (!y)?x:find_gcd(y,x%y);
}
LL dep[A];
struct Point{
LL num;
LL dep[30],gcd[30];
}p[A];
LL ans=0;
inline void DFS(LL fa,LL x){
dep[x]=dep[fa]+1;
p[x]=p[fa];
p[x].dep[++p[x].num]=dep[x],p[x].gcd[p[x].num]=val[x];
for(re int i=1;i<=p[x].num;++i){
p[x].gcd[i]=find_gcd(p[x].gcd[i],val[x]);
ans=max(ans,(dep[x]-p[x].dep[i]+1)*p[x].gcd[i]);
}
LL cnt=0;
for(re int i=1;i<=p[x].num;++i)
if(p[x].gcd[i]!=p[x].gcd[i-1]){
p[x].gcd[++cnt]=p[x].gcd[i];
p[x].dep[cnt]=p[x].dep[i];
}//去重
p[x].num=cnt;
for(re int y=head[x];y;y=road[y].nex){
LL z=road[y].to;
if(z==fa) continue;
DFS(x,z);
}
return;
}
inline void clean(){
for(re int i=1;i<=n;++i){
dep[i]=0;
p[i].num=0;
}
return;
}
signed main(){
n=in;
for(re int i=1;i<=n;++i)
val[i]=in;
for(re int i=1;i<n;++i){
LL u=in,v=in;
ljb(u,v),ljb(v,u);
rd[u]++,rd[v]++;
}
for(re int i=1;i<=n;++i)
if(rd[i]==1) root[++all]=i;
for(re int i=1;i<=all;i++){
clean();
DFS(0,root[i]);
}
printf("%lld\n",ans);
return 0;
}