二分+树型 dp hdu5682 zxa and leaf

本文介绍了一道关于树形结构的算法题,利用二分法与树形DP求解最优解。通过设定节点权值使相邻节点权值差的最大绝对值最小,采用拓扑排序自底向上进行状态更新。

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

传送门:点击打开链接

题意:一棵树n个点,其中有一些点已经有权值,现在给剩下的点安排权值,使得树中相邻两点的之差绝对值的最大值最小。

思路:如果我们首先就想到了二分,那后面很好想了。。

直接二分答案,之后check中,我们随便取1个点为根节点,然后从下向上按拓扑序做树型dp。设SL[u]和SR[u]表示节点u能填的数字的范围

我们从下往上,然后只要判断是否有交集,即有解,我们就能知道当前答案是否可以使用了。

Trick:下次再用G++交用了#prama扩栈的代码剁手。。

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 1e5 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;

struct Edge {
    int nxt, v;
} E[MX];
int Head[MX], erear;
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v) {
    E[erear].v = v;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}

int is[MX], val[MX];
LL SL[MX], SR[MX];

bool DFS(int u, int f, int x) {
    if(is[u]) SL[u]  = SR[u] = val[u];
    else SL[u] = -INF, SR[u] = INF;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(v == f) continue;
        if(!DFS(v, u, x)) return false;
        if(SL[v] != INF) SL[u] = max(SL[u], SL[v] - x);
        if(SR[v] != INF) SR[u] = min(SR[u], SR[v] + x);
    }
    if(SL[u] > SR[u]) return false;
    return true;
}
int solve() {
    int l = 0, r = 1e9, m;
    while(l <= r) {
        m = (l + r) >> 1;
        if(DFS(1, -1, m)) r = m - 1;
        else l = m + 1;
    }
    return r + 1;
}

int main() {
    int T, n, k; //FIN;
    scanf("%d", &T);
    while(T--) {
        edge_init();
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i++) is[i] = 0;
        for(int i = 1; i <= n - 1; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            edge_add(u, v); edge_add(v, u);
        }
        for(int i = 1; i <= k; i++) {
            int u, w;
            scanf("%d%d", &u, &w);
            is[u] = 1; val[u] = w;
        }
        printf("%d\n", solve());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值