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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章