POJ 3162 Walking Race(树的直径+单调队列)

题目大意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能经过一次),将这些距离按排成一个数组得到dis[1],dis[2],dis[3]……dis[n] ,在数列的dis中求一个最长的区间,使得区间中的最大值与最小值的差不超过m。

思路:先找出树的直径的两个端点来,那么树当中的其它节点能走的最大距离一定是到这个两个点当中的其中一个的。所以三遍bfs就可以求出来这个最远的距离,那么,这个最远的距离求出来之后再用两个单调队列来维护最大值和最小值。

/*************************************************************************
    > File Name:            poj_3162.cpp
    > Author:               Howe_Young
    > Mail:                 [email protected]
    > Created Time:         2015年09月05日 星期六 14时00分47秒
 ************************************************************************/

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int maxn = 1e6 + 10;
int tot, head[maxn];
struct Edge {
    int to, next, w;
}edge[maxn * 2];
typedef pair<int, int> pii;
int dis1[maxn], dis2[maxn], dis[maxn];
void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w)
{
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int pos, maxx;
bool vis[maxn];
void bfs(int u, int *dist)//从u点开始搜索到各个点的距离,保存在dist当中
{
    maxx = 0;
    queue<pii> Q;
    memset(vis, false, sizeof(vis));
    pii cur, nex;
    cur.first = u; cur.second = 0;
    vis[u] = true;
    dist[u] = 0;
    Q.push(cur);
    while (!Q.empty())
    {
        cur = Q.front();
        Q.pop();
        for (int i = head[cur.first]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if (vis[v]) continue;
            vis[v] = true;
            nex.first = v; nex.second = cur.second + edge[i].w;
            dist[v] = nex.second;
            if (maxx < nex.second)
            {
                maxx = nex.second;
                pos = v;//直径的一个端点保存在pos当中
            }
            Q.push(nex);
        }
    }
}
int Q_max[maxn], Q_min[maxn];
int n, m;
int solve()//用两个单调队列分别维护最大值和最小值
{
    int ans = 0;
    int front1, front2, tail1, tail2, i, j;
    front1 = front2 = 1; tail1 = tail2 = 0;
    for (j = 1, i = 1; i <= n; i++)
    {
        while (tail1 >= front1 && dis[Q_max[tail1]] <= dis[i]) tail1--;
        Q_max[++tail1] = i;

        while (tail2 >= front2 && dis[Q_min[tail2]] >= dis[i]) tail2--;
        Q_min[++tail2] = i;

        if (dis[Q_max[front1]] - dis[Q_min[front2]] > m)
        {
            ans = max(ans, i - j);
            while (dis[Q_max[front1]] - dis[Q_min[front2]] > m)
            {
                j = min(Q_max[front1], Q_min[front2]) + 1;
                while (tail1 >= front1 && Q_max[front1] < j) front1++;
                while (tail2 >= front2 && Q_min[front2] < j) front2++;
            }
        }
    }
    ans = max(ans, i - j);
    return ans;
}
int main()
{
    while (~scanf("%d %d", &n, &m))
    {
        int v, w;
        init();
        for (int i = 1; i < n; i++)
        {
            scanf("%d %d", &v, &w);
            addedge(i + 1, v, w);
            addedge(v, i + 1, w);
        }
        bfs(1, dis1);
        bfs(pos, dis1);
        bfs(pos, dis2);
        for (int i = 1; i <= n; i++)
            dis[i] = max(dis1[i], dis2[i]);
        printf("%d\n", solve());
    }
    return 0;
}
时间: 2024-08-06 17:48:35

POJ 3162 Walking Race(树的直径+单调队列)的相关文章

HDU 4123 Bob’s Race 树的直径+单调队列

题意: 给定n个点的带边权树Q个询问. 下面n-1行给出树 下面Q行每行一个数字表示询问. 首先求出dp[N] :dp[i]表示i点距离树上最远点的距离 询问u, 表示求出 dp 数组中最长的连续序列使得序列中最大值-最小值 <= u,输出这个序列的长度. 思路: 求dp数组就是求个树的直径然后dfs一下. 对于每个询问,可以用一个单调队列维护一下.O(n)的回答. #include <cstdio> #include <cstring> #include <strin

POJ 3162 Walking Race (树的直径,单调队列)

题意:给定一棵带边权的n个节点的树,首先要求出每个点的最长路,然后写成序列d[1],d[2]...d[n],然后求满足 区间最大值-区间最小值<=k 的最大区间长度为多少? 思路: 分两步进行: (1)最多仅需3次DFS就可以在O(n)时间内求出每个点的最长路了.先从任意点t出发遍历过有点,记录下从点t出发到每个点的最长路,然后从记录的其中一个距t最远的点root出发,再一次DFS,就可以得到离root最远的点e啦,再从e出发DFS一次,就得到所有点的最长路了.注意3次DFS的代码都是一样的喔~

POJ - 3162 Walking Race 树形dp 单调队列

POJ - 3162Walking Race 题目大意:有n个训练点,第i天就选择第i个训练点为起点跑到最远距离的点,然后连续的几天里如果最远距离的最大值和最小值的差距不超过m就可以作为观测区间,问这样的区间最长的长度? 一开始楞是没看懂题意,最讨厌这种四级题,这是在刁难我英语小能手(能用翻译的就不自己动手).而且这题感觉单调队列那里的处理更难一点,不过还是来说一说怎么树形dp取得最远距离,先画个简简单单丑丑的图 我们直接从1作为根节点开始dfs的话,可以处理1的最远距离,并且可以得出到其它节点

HDU 4123(树的直径+单调队列)

Bob’s Race Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2833    Accepted Submission(s): 917 Problem Description Bob wants to hold a race to encourage people to do sports. He has got trouble i

POJ 3162 Walking Race 树形DP+线段树

给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成为一段长度为n的数列 现在mm想要从这数列中选出一个连续的区间,要求这个区间的max-min<=m 输出最长的区间 做了一个下午 思路: 分成2个部分: 1.求出数列,即对于一棵树,求出每一个节点能到达的最远距离 2.对于这段数列,选出一个区间,使得区间的max-min<=m,并且使得区间长度尽量

POJ 3162 Walking Race 树形dp 优先队列

http://poj.org/problem?id=3162 题意 :  一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离的差小于M,问怎么取使得天数最多? 每个点的最大距离和之前http://acm.hdu.edu.cn/showproblem.php?pid=2196这道题一样 (就是求每个子树的最长子链,次长子链,然后求经过父亲节点能达到的最大值,比较一

[noi2013]快餐店 基环树dp,单调队列维护最大值和次大值

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 220000 #define inf 0x3ffffffffffffffLL typedef long long ll; int v[N],e[N],ne[N],nn,w[N]; void add(int x,int y,int z){ ne[++nn

BZOJ 1396:识别子串 SA+树状数组+单调队列

1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 381  Solved: 243[Submit][Status][Discuss] Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodcookcooksgoodfood Sample Output 1 2 3 3

hdu 4123 Bob’s Race 树的直径+rmq+尺取

Bob’s Race Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description Bob wants to hold a race to encourage people to do sports. He has got trouble in choosing the route. There are N houses and N - 1 roads