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

时间:2023-03-08 16:41:00

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

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

/*************************************************************************
> File Name: poj_3162.cpp
> Author: Howe_Young
> Mail: 1013410795@qq.com
> 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 + ;
int tot, head[maxn];
struct Edge {
int to, next, w;
}edge[maxn * ];
typedef pair<int, int> pii;
int dis1[maxn], dis2[maxn], dis[maxn];
void init()
{
tot = ;
memset(head, -, 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 = ;
queue<pii> Q;
memset(vis, false, sizeof(vis));
pii cur, nex;
cur.first = u; cur.second = ;
vis[u] = true;
dist[u] = ;
Q.push(cur);
while (!Q.empty())
{
cur = Q.front();
Q.pop();
for (int i = head[cur.first]; i != -; 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 = ;
int front1, front2, tail1, tail2, i, j;
front1 = front2 = ; tail1 = tail2 = ;
for (j = , i = ; 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]) + ;
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 = ; i < n; i++)
{
scanf("%d %d", &v, &w);
addedge(i + , v, w);
addedge(v, i + , w);
}
bfs(, dis1);
bfs(pos, dis1);
bfs(pos, dis2);
for (int i = ; i <= n; i++)
dis[i] = max(dis1[i], dis2[i]);
printf("%d\n", solve());
}
return ;
}