POJ3162 Walking Race

题解:

分为两部分,第一部分和SGU149一样。

第二个部分:给你一个数组,求最长的连续的一段子数组,使得该子数组中的最大值减去最小值不超过M

学到了新姿势。。。用2个单调队列去维护,o(n)时间就行了

具体的可以去看POJ上面的discuss。。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
#define pb push_back
#define mp make_pair
#define se second
#define fs first
#define LL long long
#define CLR(x) memset(x,0,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof(x))
#define SZ(x) ((int)(x).size())
#define FOR(it,c) for(__typeof((c).begin()) it=(c).begin();it!=(c).end();it++)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int> P;
const double eps=1e-9;
const int N=1e6+10;
const int M=1e3+10;
const LL mod=1000000007;
const int INF=1e9+10;

int n,m,cnt;
int head[N];
int down1[N],down2[N],up[N],Son[N],ans[N];
int qmax[N],qmin[N];
struct Edge{
    int v,w,nxt;
}edge[N*2];

void Init(){
    cnt=0;
    memset(head,-1,sizeof(head));
    memset(up,0,sizeof(up));
}

void AddEdge(int u,int v,int w){
    edge[cnt].v=v;
    edge[cnt].nxt=head[u];
    edge[cnt].w=w;
    head[u]=cnt++;
}

void dfs1(int u,int fa){
    down1[u]=down2[u]=0;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].v;
        int w=edge[i].w;
        if(v==fa) continue;
        dfs1(v,u);
        down2[u]=max(down2[u],down1[v]+w);
        if(down2[u]>down1[u]){
            swap(down2[u],down1[u]);
            Son[u]=v;
        }
    }
}

void dfs2(int u,int fa){
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].v;
        int w=edge[i].w;
        if(v==fa) continue;
        if(Son[u]!=v) up[v]=max(up[u],down1[u])+w;
        else          up[v]=max(up[u],down2[u])+w;
        dfs2(v,u);
    }
}

void solve(){
    int goal=0,i,j;
    int f1,r1,f2,r2;
    f1=r1=f2=r2=0;
    for(i=1,j=1;j<=n;j++){
        while(r1>f1&&ans[qmax[r1-1]]<=ans[j]) r1--;
        qmax[r1++]=j;
        while(r2>f2&&ans[qmin[r2-1]]>=ans[j]) r2--;
        qmin[r2++]=j;
        if(ans[qmax[f1]]-ans[qmin[f2]]>m){
            goal=max(goal,j-i);
            while(ans[qmax[f1]]-ans[qmin[f2]]>m){
                i=min(qmax[f1],qmin[f2])+1;
                while(r1>f1&&qmax[f1]<i) f1++;
                while(r2>f2&&qmin[f2]<i) f2++;
            }
        }
    }
    goal=max(goal,j-i);
    printf("%d\n",goal);
}

int main(){
     scanf("%d%d",&n,&m);
     Init();
     for(int i=1;i<n;i++){
         int x,y;
         scanf("%d%d",&x,&y);
         AddEdge(x,i+1,y);AddEdge(i+1,x,y);
     }
     dfs1(1,-1);
     dfs2(1,-1);
     for(int i=1;i<=n;i++) ans[i]=max(down1[i],up[i]);
     solve();
     return 0;
}
时间: 2024-12-25 22:14:28

POJ3162 Walking Race的相关文章

POJ3162 Walking Race(树形DP+尺取法+单调队列)

题目大概是给一棵n个结点边带权的树,记结点i到其他结点最远距离为d[i],问d数组构成的这个序列中满足其中最大值与最小值的差不超过m的连续子序列最长是多长. 各个结点到其他结点的最远距离可以用树形DP解决,HDU2196. 而那个最长的连续子序列可以用单调队列求..搞了挺久看了解法体会了下..简单来说就是尺取法,用两个指针[i,j]表示区间,j不停+1往前移动,然后用两个单调队列分别同时更新区间最小值和最大值,再看两个队列队首的最值差是否大于m,是的话出队并调整i值,最后用j-i+1更新答案.

[POJ3162]Walking Race(DP + 单调队列)

传送门 题意:一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离的差小于M,问怎么取使得天数最多? 求每个点到最远距离的点的距离可以用 computer 的方法. 至于第二问,可以跑一遍2个单调队列. 具体是固定左端点,右端点向右平移到最远,直到不能平移,再左端点向右平移一位.在这中间维护单调队列和更新 ans 最大值. 具体细节看代码 ——代码 1 #incl

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

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

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+线段树

给出一棵树,编号为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(树的直径+单调队列)

题目大意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能经过一次),将这些距离按排成一个数组得到dis[1],dis[2],dis[3]……dis[n] ,在数列的dis中求一个最长的区间,使得区间中的最大值与最小值的差不超过m. 思路:先找出树的直径的两个端点来,那么树当中的其它节点能走的最大距离一定是到这个两个点当中的其中一个的.所以三遍bfs就可以求出来这个最远的距离,那么,这个最远的距离求出来之后再用两个单调队列来维护最大值和最小值. /*****************

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这道题一样 (就是求每个子树的最长子链,次长子链,然后求经过父亲节点能达到的最大值,比较一

树形 DP 总结

本文转自:http://blog.csdn.net/angon823/article/details/52334548 介绍 1.什么是树型动态规划 顾名思义,树型动态规划就是在"树"的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向: 1.叶->根:在回溯的时候从叶子节点往上更新信息 2.根 - >叶:往往是在从叶往根d

poj 动态规划专题练习

http://poj.org/problem?id=2336 大意是要求一艘船将m个车运到对岸所消耗的最短时间和最小次数 定义dp[i][j]运送前i个车,当前船上有j个车所消耗的时间,非常容易得到如下ac代码的推导 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1508; const int INF=0x7f7f7f7f; int d