题目大意:给一个树形图n个点(n-1条边),XXX要练习竞走,每次选定一个点k作为开始点,每次走从k开始能走的最长的一条路径(不要重边)。要求出最长的连续的这样的k,假设连续有kx个,前提:这样kx条路径的长度的任意两个值的差不超过m。

输入: n,m; 接下来n-1行数据,每一行两个整数fi,di。第i行表示 第i+1个点与fi连着一条长度为di的边。

不用想,稍微有一点点水平的朋友都能想到,首先对于每一个点,用一个树形dp求每个点能走到的最长路径长。

树形dp

  定义结构体: max1,max1from, max2。 //记录跟这个点有关的 最长路径长、最长路径长点, 第二最长路径长.

  tree_dp1求出只走子树的边能到达的 max1,max1from, max2,max2from。

  tree_dp2补上求出加上走向父节点的边能到达的 max1,max1from, max2。

第一步还是挺简单的,相信大多数人YY一下就可以搞定的。这样从每个点开始能走到的最长路径长maxlen就出来了。这个效率是O(n)的

然后需要做的是: 求出一个最长的连续区间,对这样区间上任意两个点i,j有 |maxlen[i] - maxlen[j]| <= m。

单调队列

  /**************************************************/

  大家仔细看看这些星号就知道了。

  (O(∩_∩)O~跟你开玩笑的,还是看163到179行的代码吧)

当然第二步也是很简单的,只是要写个水水的线段树作辅助。。。。这个效率最坏O(n)*log(n)+O(n)。

PS:个人觉得这第二步可以试试枚举起点,然后二分搜索区间终点,由于线段树查询效率为log(n),所以总效率O(n)*log(n)*log(n),再加上一些剪枝可以卡过的,可是……

没有可是了,擦擦,贡献了n个WA。

献上代码,与大家共勉!

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int maxn = ;
const int maxe = maxn*;
const int maxf = (<<)^(-); struct edge{
int u,v,c;
edge(int a=,int b=,int d=){ u=a, v=b, c=d; }
}e[maxe];
int head[maxn];
int next[maxe];
int cnt;
void add(int u,int v,int c){
e[cnt] = edge(u,v,c);
next[cnt] = head[u], head[u] = cnt++;
}
struct node{
int p, plen;
int max1,max1from;
int max2,max2from;
}no[maxn];
stack<int> iden;
int visited[maxn];
void tree_dp1(int root){
memset(visited,,sizeof(visited));
while(!iden.empty()) iden.pop();
iden.push(root);
int u,v=root;
no[root].p = , no[root].plen=;
no[v].max1 = no[v].max2 = ;
no[v].max1from = no[v].max2from = -;
while(!iden.empty()){
u = iden.top();
if(!visited[u]){
for(int i=head[u];i>=;i=next[i]){
v = e[i].v;
if(v == no[u].p) continue;
no[v].p = u; no[v].plen = e[i].c;
no[v].max1 = no[v].max2 = ;
no[v].max1from = no[v].max2from = -;
iden.push(v);
}
}
else{ //当前点u访问完毕
iden.pop();
for(int i=head[u];i>=;i=next[i]){
v = e[i].v;
if(v == no[u].p) continue;
int len = e[i].c + no[v].max1;
if(len > no[u].max1){
no[u].max2 = no[u].max1, no[u].max2from = no[u].max1from;
no[u].max1 = len; no[u].max1from = v;
}
else {
if(len > no[u].max2)
no[u].max2 = len, no[u].max2from = v;
}
}
}
visited[u] = ;
}
}
int maxlen[maxn];
void tree_dp2(int root){
while(!iden.empty()) iden.pop();
iden.push(root);
int u,v;
while(!iden.empty()){
u = iden.top(); iden.pop();
for(int i=head[u];i>=;i=next[i]){
v = e[i].v;
if(v == no[u].p) continue;
iden.push(v);
}
maxlen[u] = no[u].max1;
if(u != root){
int p = no[u].p,len;
if(no[p].max1from != u)
len = no[p].max1+no[u].plen;
else
len = no[p].max2+no[u].plen;
maxlen[u] = max(maxlen[u],len);
if(len > no[u].max1){
no[u].max2 = no[u].max1, no[u].max2from = no[u].max1from;
no[u].max1 = len; no[u].max1from = p;
}
else {
if(len > no[u].max2)
no[u].max2 = len, no[u].max2from = p;
}
}
}
}
void initial(){
cnt = ;
memset(head,-,sizeof(head));
}
struct segment{
int left,right;
int maxv,minv;
}a[maxn*];
void build(int left,int right,int k){
a[k].left=left, a[k].right=right;
a[k].minv = a[k].maxv = maxlen[left];
if(left < right){
int mid=(left+right)>>,k2=k<<;
build(left,mid,k2);
build(++mid,right,k2+);
a[k].minv = min(a[k2].minv,a[k2+].minv);
a[k].maxv = max(a[k2].maxv,a[k2+].maxv);
}
}
int query_min(int l,int r,int k){
if(l <= a[k].left && r >= a[k].right)
return a[k].minv;
int mid=(a[k].left+a[k].right)>>,k2=k<<;
int k3=k2+;
if(r <= mid)
return query_min(l,r,k2);
else {
if(l > mid)
return query_min(l,r,k3);
//else
return min(query_min(l,mid,k2),query_min(mid+,r,k3));
}
}
int query_max(int l,int r,int k){
if(l <= a[k].left && r >= a[k].right)
return a[k].maxv;
int mid=(a[k].left+a[k].right)>>,k2=k<<;
int k3=k2+;
if(r <= mid)
return query_max(l,r,k2);
else {
if(l > mid)
return query_max(l,r,k3);
//else
return max(query_max(l,mid,k2),query_max(mid+,r,k3));
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m) != EOF){
initial();
int fi,di;
for(int i=;i<n;i++)
scanf("%d%d",&fi,&di), add(i+,fi,di), add(fi,i+,di);
//if(m < 0){ printf("0\n"); continue; }
int root = ;
tree_dp1(root);
//for(int i=1;i<=n;i++) printf("no[%d]: %d\t%d,\t %d\t%d\n",i,no[i].max1,no[i].max1from,no[i].max2,no[i].max2from);
tree_dp2(root);
build(,n,);
//for(int i=1;i<=n;i++) printf("no[%d]: %d\t%d,\t %d\t%d\n",i,no[i].max1,no[i].max1from,no[i].max2,no[i].max2from);
//for(int i=1;i<=n;i++) printf("maxlen[%d] = %d\n",i,maxlen[i]);
int ret = ;
int left=,right=;
int tpmin=maxlen[],tpmax=maxlen[];
while(left<=right && right<=n){
if(tpmax-tpmin <= m){
ret = max(ret,right-left+);
right++;
tpmin = min(tpmin,maxlen[right]);
tpmax = max(tpmax,maxlen[right]);
}
else {
left++;
tpmin = query_min(left,right,);
tpmax = query_max(left,right,);
}
if(ret > n - left) break;
}
printf("%d\n",ret);
}
return ;
}

  单调队列核心:最大值的单调队列保持递减,最小值得单调队列保持递增!然后记录队列中每个最值的下标,使用left、right游标追赶。下面来个个人珍藏版代码。

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int maxn = ;
const int maxe = maxn*;
const int maxf = (<<)^(-); struct edge{
int u,v,c;
edge(int a=,int b=,int d=){ u=a, v=b, c=d; }
}e[maxe];
int head[maxn];
int next[maxe];
int cnt;
void add(int u,int v,int c){
e[cnt] = edge(u,v,c);
next[cnt] = head[u], head[u] = cnt++;
}
void initial(){
cnt = ;
memset(head,-,sizeof(head));
} struct node{
int max1,max2; int max1from;
int p,pd,sc;
}no[maxn];
int visited[maxn];
void tree_dp1(int n,int root){
for(int i=;i<=n;i++) visited[i]=;
stack<int> st; st.push(root);
int u,v,d;
while(!st.empty()){
u=st.top();
//printf("u = %d, visited[u] = %d\n",u,visited[u]);
if(!visited[u]){
for(int i=head[u];i>=;i=next[i]){
v=e[i].v;
if(v == no[u].p) continue;
no[v].p=u, no[v].pd=e[i].c;
st.push(v);
}
visited[u]=;
}else {
for(int i=head[u];i>=;i=next[i]){
v=e[i].v;
if(v == no[u].p) continue;
d=e[i].c+no[v].max1;
if(d >= no[u].max1)
no[u].max2=no[u].max1, no[u].max1=d, no[u].max1from=v;
else if(d > no[u].max2)
no[u].max2=d;
}
st.pop();
}
}
}
void tree_dp2(int n,int root){
for(int i=;i<=n;i++) visited[i]=;
stack<int> st; st.push(root);
int u,v,p,d;
while(!st.empty()){
u=st.top();
//printf("u = %d, visited[u] = %d\n",u,visited[u]);
if(!visited[u]){
for(int i=head[u];i>=;i=next[i])
if(e[i].v != no[u].p)
st.push(e[i].v);
if(u != root){
p = no[u].p;
d = (no[p].max1from==u)?no[p].max2:no[p].max1;
d += no[u].pd;
if(d > no[u].max1) no[u].max2=no[u].max1, no[u].max1=d, no[u].max1from=p;
else if(d > no[u].max2) no[u].max2=d;
}
visited[u]=;
}else
st.pop();
}
}
int maxlen[maxn];
int dddl(int n,int M){
if(n <= ) return n;
if(M < ) return ;
deque<int> qmax,qmin; deque<int> idmax,idmin;
int ans=;
int left=,right=;
while(right <= n){
while(!qmax.empty() && maxlen[right] >= qmax.back())
qmax.pop_back(), idmax.pop_back();
qmax.push_back(maxlen[right]); idmax.push_back(right); while(!qmin.empty() && maxlen[right] <= qmin.back())
qmin.pop_back(), idmin.pop_back();
qmin.push_back(maxlen[right]); idmin.push_back(right);
while(qmax.front()-qmin.front() > M && left<right){
left++;
while(idmax.front() < left) idmax.pop_front(), qmax.pop_front();
while(idmin.front() < left) idmin.pop_front(), qmin.pop_front();
}
ans = max(ans,right-left+);
right++;
}
return ans;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m) != EOF){
initial();
int fi,di;
for(int i=;i<n;i++)
scanf("%d%d",&fi,&di), add(i+,fi,di), add(fi,i+,di);
int root=;
tree_dp1(n,root);
tree_dp2(n,root);
for(int i=;i<=n;i++)
maxlen[i]=no[i].max1;
printf("%d\n",dddl(n,m));
}
return ;
}

poj3162 Walking Race的更多相关文章

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

    题目大概是给一棵n个结点边带权的树,记结点i到其他结点最远距离为d[i],问d数组构成的这个序列中满足其中最大值与最小值的差不超过m的连续子序列最长是多长. 各个结点到其他结点的最远距离可以用树形DP ...

  2. POJ-3162 Walking Race (求树上两点之间最大距离)

    题目大意:给一棵树,对于所有的点,找出距它最远点的距离,然后将这些距离排成一列,找出最长的一个区间满足:其中的最大值减去最小值不大于m. 题目分析:两次dfs找出距每个节点的最远距离,然后可以通过维护 ...

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

    传送门 题意:一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离 ...

  4. 【POJ 3162】 Walking Race (树形DP-求树上最长路径问题,+单调队列)

    Walking Race   Description flymouse's sister wc is very capable at sports and her favorite event is ...

  5. POJ 3162.Walking Race 树形dp 树的直径

    Walking Race Time Limit: 10000MS   Memory Limit: 131072K Total Submissions: 4123   Accepted: 1029 Ca ...

  6. 【题解】poj 3162 Walking Race 树形dp

    题目描述 Walking RaceTime Limit: 10000MS Memory Limit: 131072KTotal Submissions: 4941 Accepted: 1252Case ...

  7. 【POJ3162】Walking Race 树形dp+单调队列+双指针

    题目大意:给定一棵 N 个节点的无根树,边有边权,现生成一个序列 d,d[i] 表示 i 号节点到树上其他节点距离的最大值.给定一个 m,求 d 序列中最大值和最小值之差不超过 m 的最长连续段的长度 ...

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

    POJ - 3162Walking Race 题目大意:有n个训练点,第i天就选择第i个训练点为起点跑到最远距离的点,然后连续的几天里如果最远距离的最大值和最小值的差距不超过m就可以作为观测区间,问这 ...

  9. POJ 3162 Walking Race 树形DP+线段树

    给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成 ...

随机推荐

  1. iOS中通知传值

    NSNotification 通知中心传值,可以跨越多个页面传值, 一般也是从后面的页面传给前面的页面.   思路: 第三个界面的值传给第一个界面. 1. 在第一个界面建立一个通知中心, 通过通知中心 ...

  2. hdu 5590 ZYB's Biology

    Problem Description After getting scores ) begins to work with biological questions.Now he give you ...

  3. 【Sharepoint】CSS与Master Page的开发与部署

    一.CSS的开发与部署相对比较简单,先是要上传CSS文件到样式库中,然后在页面模板中将上传的自定义CSS样式文件设置为所有文件的默认css文件.下面详细的介绍CSS文件的开发与部署过程. 1.编写自定 ...

  4. PHP自学2——将用户提交表单存储到外部普通文件中

    在上一节中我们已经实现了将用户的订单信息提交到服务器端,然后服务器端将提交信息返回并显示到页面上.这一节将把上一节用户的订单信息保存到外部的普通文件中(即.txt文本文件中). 本节代码将用户提交的订 ...

  5. html系列教程--embed fieldset legend figure figurecaption

    <embed> 标签:定义嵌入的内容 <embed src="" type="" /> embed属性: 1.src:嵌入内容地址 2. ...

  6. JAVA 类加载器 第14节

    JAVA 类加载器 第14节 今天我们将类加载机制5个阶段中的第一个阶段,加载,又叫做装载.为了阅读好区分,以下都叫做装载. 装载的第一步就是要获得二进制的字节流,它可以从读.class文件获得,也可 ...

  7. Rectangles

    Given two rectangles and the coordinates of two points on the diagonals of each rectangle,you have t ...

  8. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  9. php中如何输出当前服务器的(中国)当前时间

    date_default_timezone_set('PRC');//PRC是什么?PRC是中华人民共和国啊-_- echo "今天是".date("Y年m月d日&quo ...

  10. [原创]反汇编之一:和Taskmgr过不去篇(无厘头版)

    原文链接:和Taskmgr过不去篇(无厘头版) Hook入门级文章,主要想培养一下偶写文章的感觉,老鸟无视…我想看看技术文章能不能无厘头的写,如果效果不错的话,准备更上一层-----用我的原创漫画表达 ...