题目大意
给出一棵树,每条边都有一个长度。我们规定每个点的权值为从该点开始走过的一条最长的路径的长度。
求一个最长的区间[l,r],使得[l,r]里面点权的最大值和最小值只差不超过m。
n<=1000000
分析
用两个dfs求出一个点向上和向下可以走的最长路。
(可以设f[i][1],f[i][2],f[i][3],分别表示i到叶子的最长链,i到叶子的次长链,从i向上走到某个父亲,再向下的最长链。)
那更新就是….,很容易的啦233
那么我们现在就得到了a[i]=max(f[i][1],f[i][3])表示从i出发的最长链的长度。
第二步是要在a数组中求一段最长的区间满足极差小于等于m。
用两个队列分别维护最大值和最小值,将当前结点入队后,如果最大值-最小值(两个队列的队头)>m,则选一个较小的队头,以i为右节点的最长区间的左端点,就是较小的队头表示的位置+1,扔掉那个最小的以后继续扔,边扔边更新答案,扔到合法为止)
ps:bzoj现在这道题用不了啊
code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
int f[2000000][5];
int a[2000000][5];
int ls[2000000];
struct arr{
int x,y,w;
int next;
}c[2000000];
int head[2],tail[2];
int n,m,nn;
int add(int x,int y,int w)
{
nn++;
c[nn].x=x;
c[nn].y=y;
c[nn].w=w;
c[nn].next=ls[x];
ls[x]=nn;
}
int dfs1(int x,int r)
{
int i=ls[x];
while (i!=0)
{
dfs1(c[i].y,x);
if (a[x][0]<a[c[i].y][0]+c[i].w)
a[x][1]=a[x][0],a[x][0]=a[c[i].y][0]+c[i].w;
else
if (a[x][1]<a[c[i].y][0]+c[i].w)
a[x][1]=a[c[i].y][0]+c[i].w;
i=c[i].next;
}
}
int dfs2(int x,int r,int w)
{
int max1=a[r][0];
if (a[x][0]+w==a[r][0])
max1=a[r][1];
a[x][2]=max(max1,a[r][2])+w;
int i=ls[x];
while (i!=0)
{
dfs2(c[i].y,x,c[i].w);
i=c[i].next;
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=2;i<=n;i++)
{
int x=0;
int w=0;
scanf("%d%d",&x,&w);
add(x,i,w);
}
dfs1(1,0);
dfs2(1,0,0);
head[0]=head[1]=1;
tail[0]=tail[1]=0;
int ans=0;
int l=1;
for (int i=1;i<=n;i++)
{
int max1;
max1=max(a[i][0],a[i][2]);
a[i][3]=max1;
while ((a[f[tail[0]][0]][3]<=max1)&&(head[0]<=tail[0]))
tail[0]--;
while ((a[f[tail[1]][1]][3]>=max1)&&(head[1]<=tail[1]))
tail[1]--;
tail[0]++;
f[tail[0]][0]=i;
tail[1]++;
f[tail[1]][1]=i;
while (a[f[head[0]][0]][3]-a[f[head[1]][1]][3]>m)
{
if (f[head[0]][0]>f[head[1]][1])
l=f[head[1]][1]+1,head[1]++;
else
l=f[head[0]][0]+1,head[0]++;
}
ans=max(ans,i-l+1);
}
printf("%d",ans);
}