POJ 3162 Walking Race 树形DP

题意:wc建立了一个体育中心,共有N个检查点,这些检查点形成了一个树的形状。第i天,为了锻炼体质,wc从编号为i的节点出发,走到距离第i个点的最远的点。wc有一个仪器可以分析他连续几天的运动情况,但是被分析的这几天的距离的最大值和最小值的差不能超过M。请问这个仪器最长能够分析几天的结果。
思路:
第一问就是求出树上每个点的最长路径,这个就可以通过DFS得到。
第二问是求出最长的连续序列,使序列的最大值和最小值的差不大于M。
刚开始用map做的, Θ(nlogn)  超时了,所以应该用线性的单调队列做。用两个单调队列,分别保留当前的最大值和最小值,这样就是 Θ(n)  的时间复杂度了。
代码如下:

#include <cstdio>
#include <cstring>
#include <deque>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAX = 1000010;

struct edge{
    int to,w;
    edge(){}
    edge(int t, int ww):to(t),w(ww){}
} edges[MAX<<1];

int N,M;
int nxt[MAX<<1],head[MAX],tot;
int dp[MAX],tmp[MAX];


void init()
{
    memset(head,-1,sizeof(head));
    tot = 0;
}

void addedge(int u, int v, int w)
{
    edges[tot] = edge(v,w);
    nxt[tot] = head[u];
    head[u] = tot++;
}

void dfs(int u, int p, int d,int * dis)
{
    dis[u] = d;
    for(int i = head[u]; ~i; i = nxt[i]){
        edge & e = edges[i];
        if(e.to == p) continue;
        dfs(e.to,u,d+e.w,dis);
    }
}

int main(void)
{
    //freopen("input.txt","r",stdin);
    scanf("%d%d",&N,&M);
    init();
    for(int i = 2; i <= N; ++i){
        int v, w;
        scanf("%d%d",&v,&w);
        addedge(i,v,w);
        addedge(v,i,w);
    }
    dfs(1,0,0,dp);
    int p1 = 1,l1 = 0, p2 = 1, l2 = 0;
    for(int i = 1; i <= N; ++i){
        if(dp[i] > l1){
            p1 = i;
            l1 = dp[i];
        }
    }
    dfs(p1,0,0,dp);
    for(int i = 1; i <= N; ++i){
        if(dp[i] > l2){
            p2 = i;
            l2 = dp[i];
        }
    }
    dfs(p2,0,0,tmp);
    for(int i = 1; i <= N; ++i)
        dp[i] = max(dp[i],tmp[i]);
    int ans = 0;
    deque<int> d1,d2;
    for(int i = 1,j = 1; i <= N; ++i){
        while(!d1.empty() && dp[d1.back()] <= dp[i]) d1.pop_back();
        while(!d2.empty() && dp[d2.back()] >= dp[i]) d2.pop_back();
        d1.push_back(i);
        d2.push_back(i);
        if(dp[d1.front()] - dp[d2.front()] > M){
            j = min(d1.front(),d2.front()) + 1;
            while(!d1.empty() && d1.front() < j) d1.pop_front();
            while(!d2.empty() && d2.front() < j) d2.pop_front();
        }
        ans = max(ans,i-j+1);
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值