bzoj 2500 幸福的道路
LINK:幸福的道路
题目有坑.题意:一棵树 求出每个点到树上另外一个点的最大距离 在距离数组上求出最长一段连续的区间使其极差<=s 求区间长度最大值。
题目说的标号的意思就是指树上的标号 而不是重新的标号。
求树上某个点到另外一个点的最大值 没有什么好的办法 通常都是树形dp+换根 换根比较基础再维护一个次大值 在换的时候再维护一个父亲那边过来的极大值即可。
考虑第二问 一个比较显然的想法是拿到左端点寻找右端点 ST表+二分是通常的解决办法 给出一个比较强大的做法是 倍增 直接从左端点倍增到右端点即可。
再给出一个O(n)的做法是 双指针法 可以发现维护一个右指针向右扫即可 可以发现右指针单调递增。
此时需要注意的是对于区间极差的维护 采用单调队列 一个坑点:当队列为空的时候注意把i加入队列中。
这里采用双指针,可能也叫迟取法 总复杂度O(n).
const int MAXN=1000010;
int n,m,ss,len,l,r,L,R,ans;
int f[MAXN],g[MAXN],w[MAXN],c[MAXN],q[MAXN],s[MAXN];
int lin[MAXN],ver[MAXN],nex[MAXN],e[MAXN];
inline void add(int x,int y,int z)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
e[len]=z;
}
inline void dfs(int x)
{
go(x)
{
dfs(tn);
if(f[tn]+e[i]>f[x])
{
g[x]=f[x];
f[x]=f[tn]+e[i];
w[x]=tn;
}else g[x]=max(g[x],f[tn]+e[i]);
}
}
inline void dp(int x,int v)
{
go(x)
{
int ww=v+e[i];
if(tn==w[x])ww=max(ww,g[x]+e[i]);
else ww=max(f[x]+e[i],ww);
c[tn]=max(f[tn],ww);
dp(tn,ww);
}
}
int main()
{
freopen("1.in","r",stdin);
get(n);get(ss);
rep(2,n,i)
{
int get(x);int get(z);
add(x,i,z);
}
dfs(1);c[1]=f[1];dp(1,0);
s[1]=q[L=l=r=R=1]=1;int j=2;
rep(1,n,i)//对于一个i求出一个最远的j.
{
while(l<=r&&q[l]<i)++l;
while(L<=R&&s[L]<i)++L;
if(L>R)s[++R]=q[++r]=i;
while(1)
{
if(j>n)break;
int mx=c[q[l]];
int mn=c[s[L]];
mx=max(mx,c[j]);
mn=min(mn,c[j]);
if(mx-mn>ss)break;
while(l<=r&&c[j]>=c[q[r]])--r;
q[++r]=j;
while(L<=R&&c[j]<=c[s[R]])--R;
s[++R]=j;
++j;
}
ans=max(ans,j-i);
}
put(ans);return 0;
}