HDU 6867 Tree

Tree

You are given a tree consisting of n vertices numbered 1 to n rooted at node 1. The parent of the i-th vertices is pi. You can move from a vertex to any of its children. What’s more, you can add one directed edge between any two different vertices, and you can move through this edge too. You need to maximize the number of pairs (x,y) such that x can move to y through the edges after adding the edge. Note that x can also move to x.

题意
有一棵树,根是点1,现在让你添加一条有向边,求出(x_i,y_i)的对数。
其中(x_i,y_i)是一个点对,x_i可以和y_i相同。刚开始的图,点只能从父节点走到子节点,
若a是b的父节点,b是c的父节点,那么a可以走到c。

思路
容易知道,添加的一条有向边肯定是从叶子节点到根。
我们先考虑不添加这条边,那么满足的点对的数量,就是每个点的子树大小之和。
可以通过一遍dfs遍历这棵树求出来。

那么我们考虑怎么加这条边最优,想一下,从当前点加边到根节点1,这样会形成一个环。环上所有的点作为起点的话,每个起点能形成的点对都是n个。因为都能走到根节点,而根节点能走到所有其他点。那么说是环,其实是选择了一条链,然后把链的结尾接到了链头,链头显然是根节点。

接着进行第二次dfs,求出从根节点到当前节点的这条链上所有点的子树和(类似前缀和)
然后枚举每个点作为添加的有向边的起点,看哪个点的贡献最大即可,链的长度就是当前点的深度dep[i]
ma=max(ma,dep[i] * n-sum[i])
ma就是加了一条有向边后能增加的最大点对数
答案就是ma加上所有点的子树大小

当然也可以直接一次dfs 进行树型dp

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
    int to,next;
}e[1<<21];
int tot,h[1<<21];
void add(int a,int b){
    e[tot]={b,h[a]};
    h[a]=tot++;
}
ll siz[1<<21],dep[1<<21],sum[1<<21];
void dfs1(int x,ll d){
    siz[x]=1;dep[x]=d;
    for(int i=h[x];~i;i=e[i].next){
        int to=e[i].to;
        dfs1(to,d+1);
        siz[x]+=siz[to];
    }
}
void dfs2(int x,ll v){
    sum[x]=siz[x]+v;
    for(int i=h[x];~i;i=e[i].next){
        dfs2(e[i].to,sum[x]);
    }

}
int main()
{
	ios::sync_with_stdio(0);
    int T;cin>>T;
    while(T--){
        int n;cin>>n;
        tot=0;
        for(int i=1;i<=n;i++) h[i]=-1;
        for(int i=2;i<=n;i++){
            int x;cin>>x;
            add(x,i);
        }
        dfs1(1,1);///计算每个点的子树大小
        dfs2(1,0);///统计从树根到当前点的子树大小和 类似前缀和
        ll ma=0,ans=0;
        for(int i=1;i<=n;i++){
			ans+=siz[i];///每个点的子树之和
            ma=max(ma,dep[i]*n-sum[i]);///求出加上一条有向边后 能增加的最大点对数
        }
        cout<<ans+ma<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我不会c语言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值