poj 3162 Walking Race 树形dp + 单调队列

15 篇文章 0 订阅
9 篇文章 0 订阅

题目链接


题意:

给定一棵树,编号为 1 ~ n, 

先要求得树上每一点与跟它距离最远的点之间的距离,记为数组 a,

再求得 a 中最长的区间,使得该区间中的元素最大值与最小值的差值不大于给定值 m


其实纯粹是两道题拼起来,

两部分各自的典型例题分别见:

1. hdu 2196 题目链接 blog链接

题意:给定树,求树上每一点与跟它距离最远的点之间的距离

2. hdu 3530 题目链接 blog链接 

题意:给定数组,求得最长的区间,使得其中最大值与最小值的差值在给定的范围内(>=m, <=k)


这道题看似复杂,但拆开来,做了上述两道题再写这道题就相对容易了,具体的思路与做法也请参见上面两篇blog


AC代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#define maxn 1000010
using namespace std;
typedef long long LL;
struct Edge {
    int from, to, dist, ne;
    Edge(int a = 0, int b = 0, int c = 0, int d = 0) : from(a), to(b), dist(c), ne(d) {}
}edge[maxn * 2];
int next[maxn], tot, adj[maxn], cnt, lo[maxn], hi[maxn];
LL f[maxn][3], g[maxn];
inline LL max(LL a, LL b) { return a > b ? a : b; }
void add(int u, int v, int d) {
    Edge e(u, v, d, next[u]);
    edge[tot] = e;
    next[u] = tot++;
}
void dfs(int u, int fa) {
    f[u][0] = f[u][1] = 0;
    for (int i = next[u]; i != -1; i = edge[i].ne) {
        Edge e = edge[i]; int v = e.to;
        if (v == fa) continue;
        dfs(v, u);
        if (f[v][0] + (LL)e.dist > f[u][0]) {
            f[u][1] = f[u][0]; f[u][0] = f[v][0] + (LL)e.dist;
            adj[u] = v;
        }
        else if (f[v][0] + (LL)e.dist > f[u][1]) f[u][1] = f[v][0] + (LL)e.dist;
    }
}
void dfs2(int u, int fa) {
    for (int i = next[u]; i != -1; i = edge[i].ne) {
        Edge e = edge[i]; int v = e.to;
        if (v == fa) continue;
        if (adj[u] == v) f[v][2] = f[u][2] > f[u][1] ? f[u][2] : f[u][1];
        else f[v][2] = f[u][2] > f[u][0] ? f[u][2] : f[u][0];
        f[v][2] += (LL)e.dist;
        dfs2(v, u);
    }
}
int main() {
    memset(next, -1, sizeof(next));
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 2; i <= n; ++i) {
        int y, d;
        scanf("%d%d", &y, &d);
        add(i, y, d);
        add(y, i, d);
    }
    dfs(1, -1);
    dfs2(1, -1);
//    for (int i = 1; i <= n; ++i) printf("%d ", f[i][0]); printf("\n");
//    for (int i = 1; i <= n; ++i) printf("%d ", f[i][2]); printf("\n");

    for (int i = 1; i <= n; ++i) g[i] = max(f[i][0], f[i][2]);

    int f1 = 0, r1 = 0, f2 = 0, r2 = 0, l = 1, ans = 1;
    for (int i = 1; i <= n; ++i) {
        while (r1 > f1 && g[i] < g[lo[r1 - 1]]) --r1;
        lo[r1++] = i;
        while (r2 > f2 && g[i] > g[hi[r2 - 1]]) --r2;
        hi[r2++] = i;
        while (g[hi[f2]] - g[lo[f1]] > m) {
            l = min(hi[f2], lo[f1]) + 1;
            while (hi[f2] < l) ++f2;
            while (lo[f1] < l) ++f1;
        }
        if (g[hi[f2]] - g[lo[f1]] <= m) ans = max(ans, i - l + 1);
    }
    printf("%d\n", ans);
//    for (int i = 1; i <= n; ++i) printf("%d ", g[i]); printf("\n");
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值