原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6430
题目大意:
给你一棵有n个节点的树,树上的每个节点都有一个值v_i
让你求对每个节点x,对其 子节点 i 与子节点 j ,其最近公共祖先lca( i, j ) = x
而该节点x的答案为 ans[X]=max(gcd(val[i],vaj[j])) , i , j 满足 lca(i,j) = x
题解:
参考博客:https://blog.youkuaiyun.com/lifelikes/article/details/81980619
因为时第一次接触启发式合并,所以这题我是直接分析上面这位大神的代码的,之后分析如下
对于一个节点,对其最大的子树的节点信息都已经加入num与divs数组中,之后再将其他比较小的子树的节点信息一一加入两数组,查询出最大gcd。而在返回上层时,如果处理的不是父节点的最大子树,则在将这棵子树的节点信息都删除。
复杂度分析是O(n^(3/2)*log(n)) 其中的n^(1/2)是添加因数时的复杂度
AC代码(带注释):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 300010;
struct edge{
int v,next;
ll w;
};
//数的因子
vector<int> fa[N];
//邻接表
int tot;
int head[N];
edge es[N<<1];
//树的节点个数 节点的最大节点数的子树
int sz[N],sma[N];
int num[N]; //当前数字出现的次数
int divs[N]; //当前因子出现的次数
vector<int> temp; //子树节点统计的临时数组
int n;
int val[N]; //节点数字
int ans[N];
int tmp;
void add(int u,int v)
{
es[tot].v = v;
es[tot].next = head[u];
head[u] = tot;
tot ++;
}
void dfs1(int u,int pre)
{
int i;
sz[u] = 1;
for(i=head[u];i!=-1;i=es[i].next)
{
int v = es[i].v;
if(v == pre) continue;
dfs1(v,u);
sz[u] += sz[v];
if(sz[v] > sz[sma[u]])
sma[u] = v;
}
}
//增一个数字引起的各种变化
void add_node(int u)
{
int cnt,i;
num[val[u]] ++;
if(num[val[u]] == 1)
{
for(i=0;i<fa[val[u]].size();i++)
{
cnt = fa[val[u]][i];
divs[cnt] ++;
}
}
}
//将当前节点树上的所有节点遍历出
void add_tree(int u,int pre)
{
int i;
temp.push_back(u);
for(i=head[u];i!=-1;i=es[i].next)
{
int v = es[i].v;
if(v == pre)
continue;
add_tree(v,u);
}
}
//减一个数字引起的各种变化
void det_node(int u)
{
int cnt,i;
num[val[u]] --;
if(num[val[u]] == 0)
{
for(i=0;i<fa[val[u]].size();i++)
{
cnt = fa[val[u]][i];
divs[cnt] --;
}
}
}
//删除树
void det_tree(int u,int pre)
{
int i;
det_node(u);
for(i=head[u];i!=-1;i=es[i].next)
{
int v = es[i].v;
if(v == pre)
continue;
det_tree(v,u);
}
}
void dfs2(int u,int pre)
{
//遍历过程
int i,j,k;
if(sz[u] == 1)
{
ans[u] = -1;
add_node(u);
return;
}
for(i=head[u];i!=-1;i=es[i].next)
{
int v = es[i].v;
if(v==sma[u] || v==pre)
continue;
dfs2(v,u);
det_tree(v,u);
}
dfs2(sma[u],u);
//到此,num与divs中只存有sma[u]的信息
//取出树中最大因数 tmp
tmp = 0;
int cnt;
for(i=head[u];i!=-1;i=es[i].next)
{
temp.clear();
int v = es[i].v;
if(v==sma[u] || v==pre)
continue;
//得出树中有那些节点
add_tree(v,u);
for(j=0;j<temp.size();j++)
{
for(k=0;k<fa[val[temp[j]]].size();k++)
{
cnt = fa[val[temp[j]]][k];
if(divs[cnt])
tmp = max(tmp,cnt);
}
}
for(j=0;j<temp.size();j++)
add_node(temp[j]);
}
for(i=0;i<fa[val[u]].size();i++)
{
cnt = fa[val[u]][i];
if(divs[cnt])
tmp = max(tmp,cnt);
}
add_node(u);
//即为当前节点答案
ans[u] = tmp;
}
int main()
{
int i,j;
//处理每个数的因子
for(i=1;i<N;i++)
{
for(j=1;i*j<N;j++)
fa[i*j].push_back(i);
}
//构建邻接表
memset(head,-1,sizeof(head));
memset(ans,-1,sizeof(ans));
memset(num,0,sizeof(num));
memset(divs,0,sizeof(divs));
tot = 0;
scanf("%d",&n);
for(i=2;i<=n;i++)
{
int t;
scanf("%d",&t);
add(t,i);
}
for(i=1;i<=n;i++)
scanf("%d",&val[i]);
//处理树中每个节点 节点个数最大的子树是哪个
dfs1(1,-1); //O(n)
//得出答案
dfs2(1,-1);
for(i=1;i<=n;i++)
printf("%d\n",ans[i]);
return 0;
}