题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=2500
题解:
DFS,单调队列
首先有一个结论,距离树上某一个点最远的点一定是树的直径的一个端点。
然后就3个DFS求出直接并得到D[i]数组表示i点到最远的点的距离。
然后就用两个单调队列分别维护一个单减的最大值和一个单增的最小值,
以便求出以i位置结尾的最长合法区间。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 1000500 #define rint register int using namespace std; struct Edge{ int to[MAXN*2],val[MAXN*2],nxt[MAXN*2],head[MAXN],ent; Edge(){ent=2;} void Adde(int &u,int &v,int &w){ to[ent]=v; val[ent]=w; nxt[ent]=head[u]; head[u]=ent++; to[ent]=u; val[ent]=w; nxt[ent]=head[v]; head[v]=ent++; } }E; int D[MAXN]; int N,M,d1,d2,val; char gc(){ //return getchar(); static char s[300005]; static int bit=300000,p=0,len=0; if(p>=len) len=fread(s,1,bit,stdin),s[len]=EOF,p=0; return s[p++]; } void read(int &x){ static int sn; static char ch; x=0; sn=1; ch=gc(); while(ch<'0'||'9'<ch){if(ch=='-')sn=-1;ch=gc();} while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=gc();} x=x*sn; } void cmax(int &a,int b){ if(a<b) a=b; } void dfs(int u,int fa,int len,int &d){ if(len>=val) d=u,val=len; cmax(D[u],len); for(int i=E.head[u];i;i=E.nxt[i]){ if(E.to[i]==fa) continue; dfs(E.to[i],u,len+E.val[i],d); } } int solve(){ static int qmx[MAXN],qmn[MAXN],lmx=1,rmx=0,lmn=1,rmn=0,ANS=0,p=1; for(int i=1;i<=N;i++){ while(lmx<=rmx&&D[qmx[rmx]]<=D[i]) rmx--; while(lmn<=rmn&&D[qmn[rmn]]>=D[i]) rmn--; qmx[++rmx]=i; qmn[++rmn]=i; while(D[qmx[lmx]]-D[qmn[lmn]]>M){ if(qmx[lmx]<qmn[lmn]) p=qmx[lmx]+1,lmx++; else p=qmn[lmn]+1,lmn++; } cmax(ANS,i-p+1); } return ANS; } int main(){ read(N); read(M); for(rint i=2,f,d;i<=N;i++) read(f),read(d),E.Adde(f,i,d); val=0; dfs(1,0,0,d1); val=0; dfs(d1,0,0,d2); val=0; dfs(d2,0,0,d1); printf("%d",solve()); return 0; }