您的位置:首页 > 其它

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

2016-03-23 18:39 357 查看
题目大概是给一棵n个结点边带权的树,记结点i到其他结点最远距离为d[i],问d数组构成的这个序列中满足其中最大值与最小值的差不超过m的连续子序列最长是多长。

各个结点到其他结点的最远距离可以用树形DP解决,HDU2196

而那个最长的连续子序列可以用单调队列求。。搞了挺久看了解法体会了下。。简单来说就是尺取法,用两个指针[i,j]表示区间,j不停+1往前移动,然后用两个单调队列分别同时更新区间最小值和最大值,再看两个队列队首的最值差是否大于m,是的话出队并调整i值,最后用j-i+1更新答案。

当然尺取法+RMQ也是OK的。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF (1LL<<60)
#define MAXN 1000111
struct Edge{
int v,w,next;
}edge[MAXN];
int NE,head[MAXN];
void addEdge(int u,int v,int w){
edge[NE].v=v; edge[NE].w=w; edge[NE].next=head[u];
head[u]=NE++;
}
int idx[MAXN];
long long d[3][MAXN];
void dp0(int u){
for(int i=head[u]; i!=-1; i=edge[i].next){
int v=edge[i].v;
dp0(v);
if(d[0][u]<=d[0][v]+edge[i].w){
d[1][u]=d[0][u];
d[0][u]=d[0][v]+edge[i].w;
idx[u]=v;
}else if(d[1][u]<d[0][v]+edge[i].w){
d[1][u]=d[0][v]+edge[i].w;
}else if(d[1][u]<d[1][v]+edge[i].w){
d[1][u]=d[1][v]+edge[i].w;
}
}
}
void dp1(int u){
for(int i=head[u]; i!=-1; i=edge[i].next){
int v=edge[i].v;
if(idx[u]==v) d[2][v]=max(d[1][u],d[2][u])+edge[i].w;
else d[2][v]=max(d[0][u],d[2][u])+edge[i].w;
dp1(v);
}
}
struct Que{
int que[MAXN],front,rear;
bool isEmpty(){
return front==rear;
}
int getFront(){
return que[front];
}
int getRear(){
return que[rear-1];
}
void push(int a){
que[rear++]=a;
}
}mxq,mmq;
int main(){
memset(head,-1,sizeof(head));
int n,m,a,b;
scanf("%d%d",&n,&m);
for(int i=2; i<=n; ++i){
scanf("%d%d",&a,&b);
addEdge(a,i,b);
}
dp0(1);
dp1(1);
int res=0;
for(int i=1,j=1; i<=n; ++i){
d[0][i]=max(d[0][i],d[2][i]);
while(!mxq.isEmpty() && d[0][mxq.getRear()]<d[0][i]) --mxq.rear;
mxq.push(i);
while(!mmq.isEmpty() && d[0][mmq.getRear()]>d[0][i]) --mmq.rear;
mmq.push(i);
while(d[0][mxq.getFront()]-d[0][mmq.getFront()]>m){
if(mxq.getFront()<mmq.getFront()) j=mxq.getFront()+1,++mxq.front;
else j=mmq.getFront()+1,++mmq.front;
}
res=max(res,i-j+1);
}
printf("%d",res);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: