codeJan和树 (子树与其子树的差 单调栈)

本文解析了Wannafly挑战赛14D题目,介绍了一种高效求解方法。通过预处理计算每个节点的val值,并利用栈进行遍历,寻找最优解。适用于树形结构数据处理及距离和计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题Wannafly挑战赛14 D


题意

n个点的树,root为1,每条边有权

  • 定义一个结点的val==此结点为根的子树中所有其他结点到此结点的距离和
  • 定义两个结点(一个是另一个的祖宗)的sub==val差

求sub在小于等于m范围内的最大值


解析

今日做过类似的题,可以参考一下思路

  1. 首先要在N时间内预处理所有点的val:

    对于点1,有儿子2和3,val[2]和val[3]已知,且子树2和3的结点数已知,那么val[1]==val[2]+val[3]+dis[1][2]*size(2)+dis[1][3]*size(3),就是在得出2和3的基础上,加上所有1的子孙的“2或3到1的那段”


  2. 接下来是处理sub

    因为两个sub的点在一条轴上,所以可以类似于树的中序遍历做,用一个栈存数据,遍历时,往下就入栈,往上就出栈,由于这道题父结点val一定比较大,所以这个栈单调减

    在遍历到一个点i的时候,在前面找到第一个小于等于val[i]+m的点,在找得到的情况下,这个点就是点i所能配对最适合ans的祖宗


代码


#include<bits/stdc++.h>
using namespace std;
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define D long long
#define pill pair<D,D>
#define mk make_pair
#define N 100009
D read(){
    D ans=0;char last=' ',ch=getchar();
    while(!isdigit(ch))last=ch,ch=getchar();
    while(isdigit(ch))ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}

D n,m,t;
vector<pill>v[N];
D siz[N],val[N];
void getval(D p,D f){
    siz[p]=1;
    val[p]=0;
    for(int i=0;i<v[p].size();i++){
        D a=v[p][i].first,b=v[p][i].second;
        if(a==f)continue;
        getval(a,p);
        val[p]+=val[a];
        siz[p]+=siz[a];
        val[p]+=siz[a]*b;
    }
}

D sta[N];int top;
D ans;
bool cmp(D a,D b){
    return a>b;
}
void getans(D p,D f){
    sta[++top]=val[p];
    int pos=lower_bound(sta+1,sta+1+top,val[p]+m,cmp)-sta;
    if(pos!=top)ans=max(ans,sta[pos]-val[p]);
    for(int i=0;i<v[p].size();i++){
        D a=v[p][i].first;
        if(a==f)continue;
        getans(a,p);
    }
    top--;
}

int main(){
    t=read();while(t--){
        n=read(),m=read();
        for(int i=1;i<=n;i++)v[i].clear();
        for(int i=1;i<n;i++){
            D a=read(),b=read(),vv=read();
            v[a].pb(mk(b,vv)),v[b].pb(mk(a,vv));
        }
        getval(1,-1);

        top=0;ans=-1;
        getans(1,-1);
        printf("%lld\n",ans);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值