P1612 [yLOI2018] 树上的链

发布时间 2023-08-22 19:28:46作者: One_JuRuo

因为自己太憨了,所以交了好几次都没过,谢谢审核大大!!!

思路

因为这是一棵树,所以每个节点只有一个父亲,那么选定一个结点,它到根节点的路径唯一。

所以第一个思路就是暴力,对于每一个节点,直接暴力向上枚举,找到第一个满足条件的节点,然后输出长度即可。

但是显然,第一种方法很容易 TLE,所以我们需要进一步优化。

我们随便给出一棵树,选取一条链。以叶节点为例,那么找到的最长链的另一个端点应该在路径上。

那么我们很容易想到了二分,快速找到适合的节点。

但是如果二分后还是暴力把所有值加在一起的话显然还是会超时,所以我们又自然地想到了前缀和。

最后一个问题,我们该如何确定二分的范围呢?我们可以在深搜的时候用一个栈,每搜索到一个新节点,就加入栈,然后在栈中二分,当这个节点遍历完了,就出栈。

对了,需要注意的是,这道题需要开 long long

AC 代码

#include<bits/stdc++.h>
using namespace std;
struct node{long long w,c,fa;vector<long long>v;}a[100005];//因为不确定儿子个数,所以用vector储存
long long n,ans[100005],z[100005],top,l,r,mid;
void dfs(long long u)
{
	z[++top]=u;//入栈
	l=0,r=top;
   a[u].w+=a[a[u].fa].w;//前缀和
	while(l<=r)//二分
	{
		mid=l+r>>1;
		if(a[u].w-a[z[mid]].w>a[u].c) l=mid+1;
		else ans[u]=top-mid,r=mid-1;
	}
	for(long long i=0;i<a[u].v.size();i++) dfs(a[u].v[i]);//向下搜索
	--top;//出栈
}
int main()
{
	scanf("%lld",&n);
	for(long long i=2;i<=n;++i) scanf("%lld",&a[i].fa),a[a[i].fa].v.push_back(i);
	for(long long i=1;i<=n;++i) scanf("%lld",&a[i].w);
	for(long long i=1;i<=n;++i) scanf("%lld",&a[i].c);
	dfs(1);
	for(long long i=1;i<=n;++i) printf("%lld ",ans[i]);
	return 0;
}