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天每一天走的距离记录在一起,成 ...
随机推荐
- php的引用
<?php $var1 = 1; $var2 = 2; function foo(){ global $var1,$var2; $var2 = &$var1; } function te ...
- 开源消息总线ActiveMQ
一.消息中间件MOM(Message-Oriented Middleware) 消息中间件是解决异步分布式系统中通讯和排队问题的中间件技术.它利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数 ...
- java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值
在上一篇文章中.我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下.如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证 ...
- [转载]解析WINDOWS中的DLL文件---经典DLL解读
[转载]解析WINDOWS中的DLL文件---经典DLL解读 在Windows世界中,有无数块活动的大陆,它们都有一个共同的名字——动态链接库.现在就走进这些神奇的活动大陆,找出它们隐藏已久的秘密吧! ...
- UTF-8 BOM编码格式文件对SSI的影响
最近在用SSI(Server Side Includes)加载子模块的时候发现一个奇怪的现象,加载完成后的网页老是CSS有问题,被加载模块渲染后老是有空白部分.下面给出简单的示例. 文件a.html的 ...
- linux chmod使用说明
chmod是用来改变一个目录的访问权限的,一般的方式是:chmod a+rwx 其中a代表全部,还有u[目录拥有者] ,g[目录拥有组],o[其他用户] r代表读,w代表写,x代表可以执行,对应数字权 ...
- myeclipse 保存时自动格式化代码
windows -> preferences -> Java -> Editor -> Save Actions... 这就可以用到很多功能了,可以自己定义一些个保存后要处理的 ...
- Built-in Functions学习
abs(x) :返回一个数字的绝对值,可以是整形也可以是浮点型. all(iterable):返回True,如果所以迭代对象的元素为true,或者可迭代对象为空. any(iterable):如果可迭 ...
- windbg命令学习4
4.查看调用栈 k命令:显示的是一定数量的栈帧, 其中帧的数量是由.kframes命令来控制的, 默认值是256. 我们如何来判断函数的栈指针,参数地址和局部变量地址呢? 举一个简单的windbg的k ...
- Fidder 工具使用
Fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据. 使用Fiddler无论对开发还是测试来说,都有很大 ...