【树形DP+单调队列】POJ 3162 Walking Race
通道:http://poj.org/problem?id=3162
题意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能经过一次),将这些距离按排成一个数组得到d[1],d[2],d[3]……d[n] ,在数列的d中求一个最长的区间,使得区间中的最大值与最小值的差不超过m
思路:用2次dfs能求出树的直径,对于树中任意结点,到树的直径的2个端点的距离的较大者即为最长距离。得到数组d后,用2个单调队列分别维护最大与最小值,扫描d数组
代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N=1e6+5; struct Edge { int v,wei,pre; Edge(){} Edge(int a,int b,int c){v=a;pre=b;wei=c;} }edge[N*2]; int head[N],tot,n,m; int dx[N],dy[N],d[N]; int qmin[N],qmax[N]; void addEdge(int u,int v,int wei) { edge[tot]=Edge(v,head[u],wei); head[u]=tot++; } void dfs(int u,int fa,int dis,int *d) { for(int i=head[u];i!=-1;i=edge[i].pre) { int v=edge[i].v,wei=edge[i].wei; if(v!=fa) dfs(v,u,d[v]=dis+wei,d); } } void solve() { int ans=0,i,j,front1,front2,rear1,rear2; front1=rear1=0; front2=rear2=0; for(i=1,j=1;j<=n;j++) { while(rear1>front1&&d[qmax[rear1-1]]<=d[j]) rear1--; qmax[rear1++]=j; while(rear2>front2&&d[qmin[rear2-1]]>=d[j]) rear2--; qmin[rear2++]=j; if(d[qmax[front1]]-d[qmin[front2]]>m) { ans=max(ans,j-i); while(d[qmax[front1]]-d[qmin[front2]]>m) { i=min(qmax[front1],qmin[front2])+1; while(rear1>front1&&qmax[front1]<i) front1++; while(rear2>front2&&qmin[front2]<i) front2++; } } } ans=max(ans,j-i); printf("%d\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { tot=0; memset(head,-1,sizeof(head)); int x,y,i; for(int i=2;i<=n;i++) { scanf("%d%d",&x,&y); addEdge(i,x,y); addEdge(x,i,y); } dfs(1,0,d[1]=0,d); for(x=1,i=2;i<=n;i++) if(d[i]>d[x]) x=i; dfs(x,0,dx[x]=0,dx); for(y=1,i=2;i<=n;i++) if(dx[i]>dx[y]) y=i; dfs(y,0,dy[y]=0,dy); for(int i=1;i<=n;i++) d[i]=max(dx[i],dy[i]); solve(); } return 0; }