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; }