POJ 3162 Walking Race(树形DP+单调队列)
分析:首先这道题和HDU4123很像,不过这道题目数据规模很大,空间有限,不能用RMQ求解了,要用单调队列进行第二步处理。
HDU4123:http://blog.csdn.net/u013480600/article/details/21834553
依然先树形DP,求出所有的d[i]距离,然后对于m找出最长的连续区间。维护两个单调队列qmin[n]和qmax[n], qmin和qmax中存的都是元素的下标.然后每次同时往qmin和qmax尾部加上d[j],使得qmin中的元素按照d值增摆放, qmax中的元素按照d值减摆放.那么qmin中的第一个元素就是当前区间[i , j]内的最小值的下标,qmax中的第一个元素就是当前区间[i , j]内的最大值下标.
初始时i为 1,j为1.然后当添加了某个元素后使得d[qmax[front2]]-d[qmin[front1]] >M的话,说明我们刚添加的元素不符合要求,但是j-1这个元素还是符合要求的所以[i , j]内的ans=j-i.
现在问题是下一步我们应该找那些区间呢?从i+1作为左区间开始还是?因为我们刚添加的元素d[j]使得:
d[qmax[front2]]-d[qmin[front1]] >M,所以如果令i=min(qmax[front2],qmin[front1]),那么依然d[qmax[front2]]-d[qmin[front1]] >M,不合法,且这时候得到的j-i更小没有意义.
所以i必须从min(qmax[front2],qmin[front1])+1开始,这样就有可能使得最大值减最小值合法,则可以更新ans的值.
上面的理论其实也是让左区间i保持不变,看j最大能到什么位置.更新ans,然后i到下一个合法的位置上去继续尽量找到最大j.
AC代码:3469ms
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN =1000000+5;
int tot;
int longest[MAXN];
int dist[MAXN][3];
int d[MAXN];
int head[MAXN];
int n,m;
int qmin[MAXN],qmax[MAXN];
struct edge
{
int to;
int next;
int w;
}edges[MAXN*2];
void add_edge(int u,int v,int w)
{
edges[tot].to=v;
edges[tot].w=w;
edges[tot].next=head[u];
head[u]=tot++;
}
int dfs1(int u,int fa)
{
if(dist[u][0]>=0)return dist[u][0];
dist[u][0]=dist[u][1]=dist[u][2]=longest[u]=0;
for(int e=head[u];e!=-1;e=edges[e].next)
{
int v= edges[e].to;
if(v==fa)continue;
if(dist[u][0]<dfs1(v,u)+edges[e].w)
{
dist[u][1] = dist[u][0];
longest[u]=v;
dist[u][0]=dfs1(v,u)+edges[e].w;
}
else if(dist[u][1]<dfs1(v,u)+edges[e].w)
dist[u][1] = dfs1(v,u)+edges[e].w;
}
return dist[u][0];
}
void dfs2(int u,int fa)
{
for(int e=head[u];e!=-1;e=edges[e].next)
{
int v=edges[e].to;
if(v==fa)continue;
if(v==longest[u])dist[v][2]=max(dist[u][2],dist[u][1])+edges[e].w;
else dist[v][2]=max(dist[u][2],dist[u][0])+edges[e].w;
dfs2(v,u);
}
}
void solve()
{
int ans=0;
int i,j,front1,front2,rear1,rear2;
front1=rear1=front2=rear2=0;//front1是队首元素的位置,rear1是对尾元素的下一个位置
for(i=j=1;j<=n;j++)
{
while(front1<rear1 && d[qmin[rear1-1]]>=d[j])rear1--;
qmin[rear1++]=j;//qmin中存的是d[j]的下标,且qmin中按值增存
while(front2<rear2 && d[qmax[rear2-1]]<=d[j])rear2--;
qmax[rear2++]=j;//qmax中存的是d[j]的下标,且qmax中按值减存
//这样qmin中始终保持队首元素是区间[l,r]内d值最小的,qmax保证队首位d值最大的
if(d[qmax[front2]]-d[qmin[front1]]>m)//突然不合法
{
ans = max(ans,j-i);
i=min(qmin[front1],qmax[front2])+1;//此步的i必然使得最大值-最小值<=m,否则就是i已经>n了
while(front1<rear1 && qmin[front1]<i)front1++;
while(front2<rear2 && qmax[front2]<i)front2++;
}
}
ans = max(ans,j-i);
printf("%d\n",ans);
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
if(n==0&&m==0)break;
tot=0;
memset(dist,-1,sizeof(dist));
memset(head,-1,sizeof(head));
for(int i=2;i<=n;i++)
{
int v,w;
scanf("%d%d",&v,&w);
add_edge(i,v,w);
add_edge(v,i,w);
}
dfs1(1,-1);
dfs2(1,-1);
for(int i=1;i<=n;i++)
d[i] = max(dist[i][0] , dist[i][2]);
solve();
}
return 0;
}