题意:

有一棵以结点1为根的n个结点的树,每个结点有一个权值v[i],对于每个结点求出以它为LCA的两个结点的GCD的最大值。其中,

思路:

因为v[i]很小,首先预处理出1~100000的所有因子存在vector里。

法一:树上启发式合并

对于一个结点,它作为LCA就相当于求这个结点的子树中相同的因子,这么说有点难理解,比如这样子一张图,

QQ20180905-0

可以这么计算:先从结点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 协议 ,转载请注明出处!

HDU5514 容斥原理/欧拉函数的应用 Previous
自适应辛普森法 Next