题意:
有一棵以结点1为根的n个结点的树,每个结点有一个权值v[i],对于每个结点求出以它为LCA的两个结点的GCD的最大值。其中,。
思路:
因为v[i]很小,首先预处理出1~100000的所有因子存在vector里。
法一:树上启发式合并
对于一个结点,它作为LCA就相当于求这个结点的子树中相同的因子,这么说有点难理解,比如这样子一张图,
可以这么计算:先从结点1搜下去,把结点2的的因子合并到1上面并同时更新结点1的答案,再访问结点3,因为结点3还有儿子结点4,继续往下搜,将结点4的因子合并到结点3上。然后对于结点本身的因子也要放到容器里,更新答案。再把结点3合并到结点1,相同的因子说明可能是GCD,更新答案,并把1本身的因子放到容器。
因子可以放在set里,这里的启发式就体现在,当某个结点向上合并时,总是小的合并到大的。
代码:
set+启发式合并:
vector<int>fact[100010];
vector<int>v[100010];
int w[100010];
int ans[100010];
set<int>s[100010];
void init()
{
for(int i=1;i<=100000;i++)
{
for(int j=i;j<=100000;j+=i)
fact[j].push_back(i);
}
}
int merge(int x,int y)
{
if(s[x].size()<s[y].size())
swap(s[x],s[y]);
int res=-1;
set<int>::iterator it;
for(it=s[y].begin();it!=s[y].end();it++)
{
if(s[x].count(*it))
res=max(res,*it);
else
s[x].insert(*it);
}
return res;
}
void dfs(int id)
{
for(int i=0;i<v[id].size();i++)
{
int temp=v[id][i];
dfs(temp);
ans[id]=max(ans[id],merge(id,temp));//每个结点和子树合并
}
for(int i=0;i<fact[w[id]].size();i++)//将自己加入到s里
{
int temp=fact[w[id]][i];
if(s[id].count(temp))
ans[id]=max(ans[id],temp);
else
s[id].insert(temp);
}
}
int main()
{
init();
int n,x;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d",&x);
v[x].push_back(i);
}
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
memset(ans,-1,sizeof(ans));
dfs(1);
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!