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这道题一样

(就是求每个子树的最长子链,次长子链,然后求经过父亲节点能达到的最大值,比较一下)

嗯求最长连续天数这个显然n^2是不现实的,但是连续什么的会想起很久以前似曾相识的一道优先队列题...然后强行一个大根堆一个小根堆,l和r适当情况下右移,复杂度n就可以a了...

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cmath>
  6 #include<queue>
  7 using namespace std;
  8 #define pa pair<long long,int>
  9 const int maxn=1000010;
 10 int n,m;
 11 priority_queue< pa,vector< pa >,less< pa > >q1;
 12 priority_queue< pa,vector< pa >,greater< pa > >q2;
 13 struct nod{
 14     int y;
 15     long long v;
 16     int next;
 17 }e[maxn*2];
 18 int vis[maxn]={};
 19 long long f[maxn][3]={}; //0父亲 1最长 2次长
 20 long long ma[maxn]={};
 21 int f1[maxn][3]={};
 22 int head[maxn]={};
 23 int tot=0;
 24 void init(int x,int y,long long v){
 25     e[++tot].y=y;
 26     e[tot].v=v;
 27     e[tot].next=head[x];
 28     head[x]=tot;
 29 }
 30 void dfs(int x){
 31     int y;
 32     vis[x]=1;
 33     long long tmp=0;
 34     for(int i=head[x];i;i=e[i].next){
 35         y=e[i].y;
 36         tmp=e[i].v;
 37         if(!vis[y]){
 38             dfs(y);
 39             tmp+=f[y][1];
 40             if(tmp>f[x][1]){
 41                 f[x][2]=f[x][1];
 42                 f1[x][2]=f1[x][1];
 43                 f[x][1]=tmp;
 44                 f1[x][1]=y;
 45             }
 46             else if(tmp>f[x][2]){
 47                 f[x][2]=tmp;
 48                 f1[x][2]=y;
 49             }
 50         }
 51     }
 52 }
 53 void dfs2(int x,int fa,long long v){
 54     int y;
 55     long long tmp;
 56     vis[x]=1;
 57     if(f1[fa][1]==x){
 58         f[x][0]=v+max(f[fa][0],f[fa][2]);
 59     }
 60     else{
 61         f[x][0]=v+max(f[fa][0],f[fa][1]);
 62     }
 63     ma[x]=max(f[x][0],f[x][1]);
 64     for(int i=head[x];i;i=e[i].next){
 65         y=e[i].y;
 66         tmp=e[i].v;
 67         if(!vis[y]){
 68             dfs2(y,x,tmp);
 69         }
 70     }
 71 }
 72 long long mab(long long x){
 73     if(x<0){
 74         return -x;
 75     }
 76     return x;
 77 }
 78 int main(){
 79     scanf("%d%d",&n,&m);
 80     int y;
 81     long long v;
 82     for(int i=1;i<n;i++){
 83         scanf("%d%lld",&y,&v);
 84         init(i+1,y,v);
 85         init(y,i+1,v);
 86     }dfs(1);
 87     memset(vis,0,sizeof(vis));
 88     dfs2(1,0,0);
 89     int ans=0;
 90     int l=1,r=0;
 91     for(int i=1;i<=n;i++){
 92         q1.push(make_pair(ma[i],i));
 93         q2.push(make_pair(ma[i],i));
 94         r++;
 95         int id1=q1.top().second,id2=q2.top().second;
 96         while(id1<l){
 97             q1.pop();
 98             id1=q1.top().second;
 99         }
100         while(id2<l){
101             q2.pop();
102             id2=q2.top().second;
103         }
104         long long z1=q1.top().first,z2=q2.top().first;
105         while(mab(z1-z2)>=m){
106             l++;
107             while(id1<l){
108                 q1.pop();
109                 id1=q1.top().second;
110             }
111             while(id2<l){
112                 q2.pop();
113                 id2=q2.top().second;
114             }
115             z1=q1.top().first,z2=q2.top().first;
116         }ans=max(ans,r-l+1);
117     }
118     cout<<ans<<endl;
119     return 0;
120 }

时间: 2024-08-01 13:37:37

POJ 3162 Walking Race 树形dp 优先队列的相关文章

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

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

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 (树的直径,单调队列)

题意:给定一棵带边权的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(树的直径+单调队列)

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

poj 2324 Anniversary party(树形DP)

/*poj 2324 Anniversary party(树形DP) ---用dp[i][1]表示以i为根的子树节点i要去的最大欢乐值,用dp[i][0]表示以i为根节点的子树i不去时的最大欢乐值, ---于是当i去时,i的所有儿子都不能去:dp[i][1]=sum(dp[j][0])+a[i],其中j是i的儿子节点. ---当i不去时,i的儿子可去也可不去:dp[i][0]=sum(max(dp[j][0],dp[j][1])),j是i的儿子节点 ---边界条件:当i时叶子节点时,dp[i][

[POJ 1947]Rebuilding Roads (树形dp)

题目链接:http://poj.org/problem?id=1947 题目大意:给你一棵树,树上N个节点.问最少拆掉多少条边使得存在一个联通块,有P个节点. 树形dp,设计状态:dp[u][i]代表以u为根节点的剩下i个节点最少需要拆掉多少条边. 状态转移:dp[u][i+j] = min(dp[u][i+j],dp[u][i]+dp[v][j]-1); 其中v是u的儿子节点. 相当于在树上跑01背包,即每个儿子节点去掉剩下j个的但是要连上u-v边,或者不去掉剩下j个的. 代码: 1 impo

POJ 1947 Rebuilding Roads (树形dp 经典题)

Rebuilding Roads Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 9499   Accepted: 4317 Description The cows have reconstructed Farmer John's farm, with its N barns (1 <= N <= 150, number 1..N) after the terrible earthquake last May. The

hdu 4123 Bob’s Race(树形dp+RMQ)

题目链接:hdu 4123 Bob's Race 题目大意:一个城镇有N个住户,N-1条路连接两个住户,保证N个住户联通,M次询问,给定N条边的信息,包括连 接的住户序号以及路的长度.然后是M次询问,每次询问Q,要求找到最长的连续序号,使得Max(dis[i]) - Min(dis[i]) ≤ Q(l≤i≤r),输出最大的r-l+1.dis[i]为从第i个住户出发,不重复走过路能移动的最远距离. 解题思路:树形dp,通过两次dfs,第1次处理出每个节点中孩子节点移动的最长距离和第二长距离,第2次

POJ 2152 Fire (树形DP,经典)

题意:给定一棵n个节点的树,要在某些点上建设消防站,使得所有点都能够通过某个消防站解决消防问题,但是每个点的建站费用不同,能够保证该点安全的消防站的距离上限也不同.给定每个点的建站费用以及最远的消防站距离上限,求保证该树安全的最小花费. 思路: 要选择部分点来建站解决消防问题,而总花费是有最优解的. 如何进行树形DP? 假设某点t的所有子树的消防问题都解决,而且已经获得最优解了,那么现在考虑的是点t的最优解问题,点t可以依靠任何点只要不超过距离限制即可,那枚举一下所有点试试,一旦t依靠某个点j解