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;
}
posted @ 2020-04-08 16:07  chdy  阅读(87)  评论(0编辑  收藏  举报