一开始看题目,完全没有思路,想到用dfs,但一直不知道处理。看了别人的思路,最后写了很久,才AC。
大概思路:
对于根节点,要么为0,那么不为0,而且根节点一定处于任意一条路中,利用这个性质,我们可以对根节点的状态进行枚举。
当根节点为0时,那么每条路上的把一个数变成0的机会已经用掉了。那么只有对此时的树进行一次遍历,同时求最大公约数,并记录。
当根节点不为0时,那么每一条路上的最大公约数一定为根节点的约数,因此可以把根节点的约数处理出来,然后再进行一次遍历,对于遍历到的每一个点,用根节点的约数去和它进行处理。如果到当前节点时,每一个约数在这条路径上满足的次数与当前点的深度之差小于1。那么,这个点就可以以这个约数为到该点路径的最大公约数。同时比较,取最大。
注意存边时,要存双向,因为输入并不保证,边是从上到下。
后面就是dfs。
#include <cstdio>
#include <cstring>
#include<vector>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=2e5+5;
int n,ans[N],a[N],d[N];
vector<int>tree[N];
vector<int>gcd;
bool vis[N];
void dfs1(int pos,int g)
{
vis[pos]=true;
for(int i=0;i<tree[pos].size();i++)
{
int p=tree[pos][i];
if(vis[p])
continue;
if(g>0)
ans[p]=__gcd(g,a[p]);
else
ans[p]=a[p];
dfs1(p,ans[p]);
}
}
void dfs2(int pos,int dep)
{
vis[pos]=true;
for(int i=0;i<tree[pos].size();i++)
{
int p=tree[pos][i];
if(vis[p])
continue;
for(int j=0;j<gcd.size();j++)
{
if(a[p]%gcd[j]==0)
d[j]++;
if(dep-d[j]<=1)
ans[p]=max(ans[p],gcd[j]);
}
dfs2(p,dep+1);
for(int j=0;j<gcd.size();j++)
{
if(a[p]%gcd[j]==0)
d[j]--;
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
memset(d,0,sizeof(d));
gcd.clear();
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
tree[i].clear();
}
int x,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
tree[x].push_back(y);
tree[y].push_back(x);
}
ans[1]=a[1];
memset(vis,false,sizeof(vis));
vis[1]=true;
dfs1(1,0);
for(int i=1;i*i<=a[1];i++)//把根节点的所有因子求出
{
if(a[1]%i==0)
{
gcd.push_back(i);
if(a[1]/i!=i)
gcd.push_back(a[1]/i);
}
}
vis[1]=true;
memset(vis,false,sizeof(vis));
dfs2(1,1);
for(int i=1;i<=n;i++)
{
if(i<n)
printf("%d ",ans[i]);
else
printf("%d\n",ans[i]);
}
}
return 0;
}