Problem E. TeaTree——树上启发式合并

A - Problem E. TeaTree

Recently, TeaTree acquire new knoledge gcd (Greatest Common Divisor), now she want to test you.
As we know, TeaTree is a tree and her root is node 1, she have n nodes and n-1 edge, for each node i, it has it’s value v[i].
For every two nodes i and j (i is not equal to j), they will tell their Lowest Common Ancestors (LCA) a number : gcd(v[i],v[j]).
For each node, you have to calculate the max number that it heard. some definition:
In graph theory and computer science, the lowest common ancestor (LCA) of two nodes u and v in a tree is the lowest (deepest) node that has both u and v as descendants, where we define each node to be a descendant of itself.
Input
On the first line, there is a positive integer n, which describe the number of nodes.
Next line there are n-1 positive integers f[2] ,f[3], …, f[n], f[i] describe the father of node i on tree.
Next line there are n positive integers v[2] ,v[3], …, v[n], v[i] describe the value of node i.
n<=100000, f[i]<i, v[i]<=100000
Output
Your output should include n lines, for i-th line, output the max number that node i heard.
For the nodes who heard nothing, output -1.
Sample Input
4
1 1 3
4 1 6 9

题解:

第一次写树上的启发式合并。可以用普通的优化让你n2变成严格nlog,解决一些类似(树上数颜色,树上查众数)这样的问题。这道题,我们先预处理出我们的因数,然后合并的时候判断下。

有的同学可能会有疑问:为什么要清空呢?
由于空间的限制,我们不可能对于每一个节点开一个数组来记录数据,只能开一个全局数组。
在这个全局数组内,如果不清空,就会影响到别的子树,于是导致答案错误。
然而可以发现,统计儿子节点时最后那个节点其实没有必要清空,因为它不再会影响到它的兄弟节点。
这也正是接下来要讲到的优化方法。

思考优化:对于节点x,可以在做子树答案时保留最后一棵子树v的数据不清空,然后统计x的答案时绕过v节点统计别的子树。那么v选哪个呢?当然是选size最大的。
于是,我们得到了一个优化后的做法:对于节点x,先统计轻儿子的答案,并将它们的数据清除;然后统计重儿子的答案,保留数据;最后遍历其他轻儿子及其子树,把它们的数据与重儿子合并。

#include <bits/stdc++.h>
//#define int long long
typedef long long ll;
using namespace std;
const int N=200005;
set<int> st[N];
vector<int> g[N];
int ans[N];
int ne[N<<1],head[N<<1],cnt,e[N<<1],w[N];
void add(int a,int b)
{
    e[cnt]=b,ne[cnt]=head[a],head[a]=cnt++;
}
int Merge(int u,int v)
{
    int res=-1;
    if(st[u].size()<st[v].size()) swap(st[u],st[v]);
    for(auto it:st[v]){
        if(st[u].count(it)) res=max(res,it);
        else st[u].insert(it);
    }
    st[v].clear();
    return res;
}
void dfs(int u)
{
    for(int i=head[u];~i;i=ne[i]){
        int j=e[i];
        dfs(j);
        ans[u]=max(ans[u],Merge(u,j));
    }
    for(auto it:g[w[u]]){
        if(st[u].count(it)){
            ans[u]=max(ans[u],it);
        }else st[u].insert(it);
    }
}
int main()
{
    for(int i=1;i<N;i++){
        for(int j=i;j<N;j+=i){
            g[j].push_back(i);
        }
    }
    int n;
    while(cin>>n) {
        cnt=0;
        memset(ans, -1, sizeof ans);
        memset(head, -1, sizeof head);
        for (int i = 2; i <= n; i++) {
            int tmp;
            scanf("%d", &tmp);
            add(tmp, i);
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", &w[i]);
        }
        dfs(1);
        for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
        for(int i=1;i<N;i++) {
            st[i].clear();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值