[Bzoj2500]幸福的道路(树上最远点)

本文解析了一道名为“幸福的道路”的算法题目,介绍了如何通过树形结构找出每条路径的最大幸福值,并使用优先队列来解决连续天数内的幸福值波动限制问题。

2500: 幸福的道路


 

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 474  Solved: 194
[Submit][Status][Discuss]

Description


 

 
小T与小L终于决定走在一起,他们不想浪费在一起的每一分每一秒,所以他们决定每天早上一同晨练来享受在一起的时光.
他们画出了晨练路线的草图,眼尖的小T发现可以用树来描绘这个草图.
他们不愿枯燥的每天从同一个地方开始他们的锻炼,所以他们准备给起点标号后顺序地从每个起点开始(第一天从起点一开始,第二天从起点二开始……). 而且他们给每条道路定上一个幸福的值.很显然他们每次出发都想走幸福值和最长的路线(即从起点到树上的某一点路径中最长的一条).
他们不愿再经历之前的大起大落,所以决定连续几天的幸福值波动不能超过M(即一段连续的区间并且区间的最大值最小值之差不超过M).他们想知道要是这样的话他们最多能连续锻炼多少天(hint:不一定从第一天一直开始连续锻炼)?
现在,他们把这个艰巨的任务交给你了!

Input


 

第一行包含两个整数N, M(M<=10^9).
第二至第N行,每行两个数字Fi , Di, 第i行表示第i个节点的父亲是Fi,且道路的幸福值是Di.

Output


 

最长的连续锻炼天数

Sample Input

3 2
1 1
1 3

 


Sample Output


 

3

 


数据范围:


50%的数据N<=1000
80%的数据N<=100 000
100%的数据N<=1000 000
 

HINT


 

 

分析:


比较容易分析的题目。

求每个点的树上最远距离

后面的处理连续天数因为具有传递性是O(n)的。

用优先队列模拟出multiset,总复杂度O(nlogn)

AC代码:


 

# include <iostream>
# include <cstdio>
# include <queue>
using namespace std;
typedef long long LL;
const int N = 1e6 + 12;
int head[N],dt,n,mn[N],Log[22];LL m,f1[N],f2[N],g[N];
struct Edge{
    int to,nex;LL w;
}edge[N];
struct Heap{
  priority_queue<LL> A,B;
  void insert(LL x){A.push(x);}
  void erase(LL x){B.push(x);}
  void pop(){while(!B.empty() && A.top() == B.top())A.pop(),B.pop();}
  LL top(){pop();return A.top();}
  int size(){return A.size() - B.size();}
}A,B;
void AddEdge(int u,int v,LL w)
{
    edge[++dt] = (Edge){v,head[u],w};
    head[u] = dt;
}
void dfs(int u)
{
    f1[u] = f2[u] = 0;
    for(int i = head[u];i;i = edge[i].nex)
    {
        dfs(edge[i].to);
        LL tmp = f1[edge[i].to] + edge[i].w;
        if(tmp >= f1[u])
        {
            f2[u] = f1[u];
            f1[u] = tmp;
        }
        else f2[u] = max(f2[u],tmp);
    }
}
void Dfs(int u)
{
    for(int i = head[u];i;i = edge[i].nex)
    {
        if(f1[u] == f1[edge[i].to] + edge[i].w)
        g[edge[i].to] = max(f2[u],g[u]) + edge[i].w;
        else g[edge[i].to] = max(f1[u],g[u]) + edge[i].w;
        Dfs(edge[i].to);
    }
}
int main()
{
   scanf("%d %lld",&n,&m);int x;LL y;
   for(int i = 2;i <= n;i++)scanf("%d %lld",&x,&y),AddEdge(x,i,y);
   dfs(1);Dfs(1);
   for(int i = 1;i <= n;i++)g[i] = max(g[i],f1[i]);
   A.insert(g[1]);B.insert(-g[1]);int r = 2,ans = 0;
   for(int i = 1;i <= n;i++)
   {
       LL mi = r == i ? g[i] : -B.top(),mx = r == i ? g[i] : A.top();
       while(r <= n)
       {
           mx = max(mx,g[r]);
           mi = min(mi,g[r]);
           if(mx - mi <= m)A.insert(g[r]),B.insert(-g[r]),r++;
           else break;
       }
       A.erase(g[i]);B.erase(-g[i]);
       ans = max(ans,r - i);
   }
   printf("%d\n",ans);
}

 

转载于:https://www.cnblogs.com/lzdhydzzh/p/8758110.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值