codeJan和树 (dfs+倍增)

链接:https://www.nowcoder.com/acm/contest/81/D
来源:牛客网

题目描述
codeJan有一天脑洞大开,想到一个有趣的问题。给一个固定根为1号结点的树,定义一个子树的beauty是这个子树的根节点到所有这棵树上其他节点的距离和,叶子节点的beauty是0。定义一个子树的sub-beauty是这个子树的beauty值减去这个子树的某一个子树(不包括自身)的beauty值。显然一个子树的beauty值是唯一的,而sub-beauty值可以有很多个。codeJan想要知道所有子树的所有sub-beauty中不超过m的最大值。
输入描述:

第一行是一个T≤20代表测试组数。每组测试的第一行包含两个正整数是n,m(n≤105,m≤108),接下来n−1
行每行包含三个正整数a b d,分别表示a结点和b结点之间的距离是d,a,b∈[1,n],1≤d≤103。请注意每棵树的根节点都是1号结点,并且保证输入合法。

输出描述:

对于每组测试样例输出一个整数表示所有子树sub-beauty中不超过m的最大值。如果所有子树的sub-beauty都大于m,输出-1。

示例1
输入

3
3 4
1 2 1
1 3 2
3 4
1 2 3
1 3 2
4 6
1 2 2
2 3 5
3 4 2

输出

3
-1
6

思路:
不从每个祖先树考虑子树,而是从子树考虑祖先树,然后用倍增查找下就行
accode

#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+5;
int n,m;
LL dp[maxn];
int fa[maxn];
int bz[maxn][31];
int head[maxn];
int tot;
int dep[maxn];
LL mx[maxn];
struct node
{
    int v;
    int net;
    LL va;
}E[maxn*2];
void init()
{
    memset(head,-1,sizeof(head));
    tot = 0;
}
void build(int u,int v,LL va)
{
    E[tot].v = v;
    E[tot].va = va;
    E[tot].net = head[u];
    head[u] = tot++;
}
int dfs(int u,int deep,int pa)
{
   // cout<<"fwfw"<<endl;
    dep[u] = deep;
    int cnt = 0;
    for(int i = head[u];~i;i = E[i].net){
        int v = E[i].v;
        if(v==pa) continue;
        fa[v] = u;
        int tmp = dfs(v,deep+1,u);
        dp[u] += dp[v]+tmp*E[i].va;
        cnt+=tmp;
    }
    if(cnt==0){
        dp[u] = 0;
        return 1;
    }
    return cnt+1;
}
void BZ()
{
    for(int i = 1;i<=n;i++){
        bz[i][0] = fa[i];
    }
    for(int i = 1;i<31;i++){
        for(int j = 1;j<=n;j++){
            bz[j][i] = bz[bz[j][i-1]][i-1];
        }
    }
}
LL getans(int x)
{
    LL ret = 0;
    int xx = x;
    for(int i = 30;i>=0;i--){
        if(bz[xx][i] == 0) continue;
        if(dp[bz[xx][i]]-dp[x]<=m){
            xx = bz[xx][i];
        }
    }
    if(xx==x) return -1;
    return dp[xx]-dp[x];
}
int t;
int main()
{
    scanf("%d",&t);
    while(t--){
        init();
        memset(bz,0,sizeof(bz));
        memset(mx,-1,sizeof(mx));
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        fa[1] = 0;
        for(int i = 0;i<n-1;i++){
            int u,v;
            LL d;
            scanf("%d%d%lld",&u,&v,&d);
            build(u,v,d);
            build(v,u,d);
            //fa[v] = u;
        }
        dfs(1,0,-1);
        BZ();
        for(int i = 2;i<=n;i++){
            mx[i] = getans(i);
        }
        LL ans = -1;
        for(int i = 1;i<=n;i++){
            ans = max(ans,mx[i]);
        }
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值