cf 1099D/1098A

这是一题思维题,但我思维不好所以不会,来记录一下

很明显,对于每一个为-1的结点,它的取值范围只能为[它的祖先,min(它的儿子)],如果不满足这个必定输出-1(每个结点都必须满足这个,否则-1)。因为我们要求的是sigema(ai),所以取值只能是两个边界,取中间的数必定会对答案造成贡献(因为我们计算ai的方式是s[i]-s[fa]),那我们应该取两者的哪一个呢?答案是后者,如果取的是祖先的s,那么这个结点最大的儿子的ai会非常大,为了让这个数更小,我们可以取最小的儿子的si。这样虽然会让这个结点的ai不为0,但因为si递增,所以这样做会令答案更优。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<list>
#define int long long
using namespace std;
list<int>lis[100005];
int fa[100005],s[100005],n,vis[100005];
int ans;
const int inf=0x3f3f3f3f;
void dfs1(int rt)
{
	s[fa[rt]]=min(s[fa[rt]],s[rt]);//不能由最底一直推向最顶,否则不能判断-1 
	vis[rt]=1;
	for(list<int>::iterator it=lis[rt].begin();it!=lis[rt].end();it++)
	{
		if(!vis[*it])
			dfs1(*it);
	}
}
void dfs2(int rt)
{
	vis[rt]=1;
	if(s[rt]<s[fa[rt]]){
		puts("-1");exit(0);
	}
	if(s[rt]!=inf)
	ans+=s[rt]-s[fa[rt]];
	for(list<int>::iterator it=lis[rt].begin();it!=lis[rt].end();it++)
	{
		if(!vis[*it])dfs2(*it);
	}
	
}
signed main()
{
	cin>>n;
	for(int i=2;i<=n;i++)
	{
		scanf("%lld",&fa[i]);
		lis[fa[i]].push_back(i);
		lis[i].push_back(fa[i]);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&s[i]);
		if(s[i]==-1)s[i]=inf;
	}
	dfs1(1);
	memset(vis,0,sizeof(vis));
	/*for(int i=1;i<=n;i++)
	{
		cout<<s[i]<<endl;
	}*/
	dfs2(1);
	cout<<ans;
}

猜你喜欢

转载自blog.csdn.net/qq_37073764/article/details/106866736
今日推荐