poj3162 Walking Race
题目大意:给一个树形图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的更多相关文章
- POJ3162 Walking Race(树形DP+尺取法+单调队列)
题目大概是给一棵n个结点边带权的树,记结点i到其他结点最远距离为d[i],问d数组构成的这个序列中满足其中最大值与最小值的差不超过m的连续子序列最长是多长. 各个结点到其他结点的最远距离可以用树形DP ...
- POJ-3162 Walking Race (求树上两点之间最大距离)
题目大意:给一棵树,对于所有的点,找出距它最远点的距离,然后将这些距离排成一列,找出最长的一个区间满足:其中的最大值减去最小值不大于m. 题目分析:两次dfs找出距每个节点的最远距离,然后可以通过维护 ...
- [POJ3162]Walking Race(DP + 单调队列)
传送门 题意:一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离 ...
- 【POJ 3162】 Walking Race (树形DP-求树上最长路径问题,+单调队列)
Walking Race Description flymouse's sister wc is very capable at sports and her favorite event is ...
- POJ 3162.Walking Race 树形dp 树的直径
Walking Race Time Limit: 10000MS Memory Limit: 131072K Total Submissions: 4123 Accepted: 1029 Ca ...
- 【题解】poj 3162 Walking Race 树形dp
题目描述 Walking RaceTime Limit: 10000MS Memory Limit: 131072KTotal Submissions: 4941 Accepted: 1252Case ...
- 【POJ3162】Walking Race 树形dp+单调队列+双指针
题目大意:给定一棵 N 个节点的无根树,边有边权,现生成一个序列 d,d[i] 表示 i 号节点到树上其他节点距离的最大值.给定一个 m,求 d 序列中最大值和最小值之差不超过 m 的最长连续段的长度 ...
- POJ - 3162 Walking Race 树形dp 单调队列
POJ - 3162Walking Race 题目大意:有n个训练点,第i天就选择第i个训练点为起点跑到最远距离的点,然后连续的几天里如果最远距离的最大值和最小值的差距不超过m就可以作为观测区间,问这 ...
- POJ 3162 Walking Race 树形DP+线段树
给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成 ...
随机推荐
- ShareSDK的简化压缩和使用样例
share sdk 太大了 于是自己 開始简化, 删除了 ipad 的图片和 framework , 压缩了 sharesdk 中的 图片,用几k的图片 替换了几个 200多k 的图片 仅仅保留 ...
- wcf系列学习5天速成——第三天 事务的使用
今天是速成的第三天,再分享一下WCF中比较常用的一种技术,也就是”事务“. 在B2B的项目中,一般用户注册后,就有一个属于自己的店铺,此时,我们就要插入两张表, User和Shop表. 当然,要么插入 ...
- 给自己保存份CSS初始值样式
@charset "utf-8";body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,text ...
- javascript高级程序设计一(1-80)
源代码研究,实例:http://fgm.cc/learn/ js面试知识点: 1:原生.闭包.上下文.call.apply.prototype. 2:jsonp:用script标签实现跨域.xss:j ...
- JS 精粹( 函数)
函数是对象,它与其它对象唯一的不同是它可以调用.函数可实现:代码复用.信息隐藏.代码组合调用. 建立函数时会建立:上下文.调用函数的代码.每个函数(除Function.prototype)都会有一个原 ...
- Oracle的实例占用内存调整
1.操作 (oracle使用内存约等于 SGA+PGA,所以可以减少SGA与PGA解决你的问题,生产库慎用)alter system set sga_max_size=100m scop ...
- java中int和Integer的区别
Integer与int的种种比较你知道多少? 转载自http://www.cnblogs.com/liuling/archive/2013/05/05/intAndInteger.html 如果面试 ...
- U3D学习使用笔记(四)
1.Unity3D项目实战笔记(5):延时功能的几种实现 http://www.xifarm.com/invoke/ 2.动画运行出屏幕范围时消失 Animation CullingType 3.No ...
- C语言版推箱子
推箱子源代码初步: #include<stdio.h> #include<conio.h> #include<stdlib.h> #define boolean i ...
- Linux学习之服务器端口查看的方法
1.用netstat查看: [grid@rac121 admin]$ netstat -anp | grep oracle (Not all processes could be identified ...