题解——草莓(树上切割)

分草莓(树上切割)

自述:判断放进了循环,debug了1.5个小时,最后还是请sxk大佬才改出,感谢sxk大佬

题目描述

院子里有一颗又高又大的草莓树,草莓树有n个节点,每个节点都结了一个草莓,吃掉第i个结点的草莓可以得到ai的营养值,由于草莓可能会坏掉,所以ai可能为负值,也可能为0。
现在要砍掉这颗树的两条边,使树变成三份,并且使得三份各自草莓营养值的和恰好一样。请问是否有这样的方法呢?如果有,请输出YES,否则输出NO。

输入输出

第一行一个数t,表示测试点的个数。
接下来t组:
每组第一行一个数n,表示结点的个数。
接下来n行,每行两个数fai和ai,表示第i个结点的父亲是fai,第i个结点有营养值为ai的草莓,根节点的fai记为0.
【输出格式】
输出t行,如果第t组有解,则输出YES,否则输出NO

样例

2
6
2 4
0 5
4 2
2 1
1 1
4 2
6
2 4
0 6
4 2
2 1
1 1

题目分析

这是一道可以体现Dp思想的爆搜题,(就是用不到Dp的Dp题)
对于本题,我们考虑,既然要分成 3 份,那么对于所有营养值之和不为 3 的倍数的数据,输出“NO”。
这棵树满足一个神奇的性质:只要有子树大小等于营养值之和/3,那么这棵子树可以删去,且对后面判断剩下子树时无后效性(Dp思想)。
在这里给出证明:
在这里定义 子树大小等于营养值之和/3 为合法子树。
对于任意一棵合法的树,根节点所连的边下可能直接有一棵合法子树,也可能是包含了另一颗合法子树的合法子树(删去包含的合法子树后)。那么在删去一颗合法子树后,剩下的一定可以继续分出合法子树。否则全树不合法。
对于存在大于3个合法子树的树,两最近合法子树节点之间的营养值之和为 0 ,否则这两个合法子树不存在。而这营养值之和为零的一段节点,可以放于相邻的任意一颗子树。所以必定可以分出 3 棵合法子树 。
如不满足上述条件,显然无法分割。
证毕。

AC代码

#include<bits/stdc++.h>
using namespace std;
const  int  MAXN = 200005 ;
inline int read()
{
    int s = 0,w = 1;
    char g = getchar();
    while(g<'0'||g>'9'){if(g=='-')w*=-1;g = getchar();}
    while(g>='0'&&g<='9'){s = s*10+g-'0';g = getchar();}
    return s*w;
}
int  N , T , S , size[ MAXN ] , sum[ MAXN ] , ccc ;
int  tot , nex[ MAXN ] , to[ MAXN ] , head[ MAXN ] ;
void  add( int  x , int  y  ){
	tot++ ;
	to[ tot ] =  y ;
	nex[ tot ] = head[ x ] ;
	head[ x ] = tot ;
}
void  prepare_(){
	N = read() ;
	for( register int  i = 1 ; i <= N ; i ++ ){
		int m1 = read() ;
		size[ i ] = read() ; 
		add( m1 , i ) ; S+=size[ i ] ; 
	}
	for( register int  i = 1 ; i <= N ; i ++ )sum[ i ] = size[ i ] ;
}
void  dfs( int u ){
	for( register int  i = head[ u ] ; i ; i = nex[ i ] ){
		dfs( to[ i ] ) ;
		sum[ u ] += sum[ to[ i ] ] ;	
	}
	if( sum[ u ] == S/3 )sum[ u ] = 0 , ccc++ ;
}
int  Ans_check(){
	if( ccc >= 3 )printf("YES\n");
	else printf("NO\n");
}
void clear(){
	memset( head , 0 , sizeof(head) );
	S = tot = ccc = 0 ;
}
void init_(){
	freopen("strawberry.in","r",stdin);
	freopen("strawberry.out","w",stdout);	
	T = read() ;
}
int  main()
{
	init_();
	while( T-- ){
		prepare_();
		if( S%3 ){ printf("NO\n") ; clear() ; continue ; }
		dfs( 0 ) ;
		Ans_check();
		clear();
	}
	return  0 ;
}
相信下一次有人看这篇博客也是我退役的时候了。

有疑惑和建议,可以留下评论或私我。

如果你喜欢我的文章,请点赞支持,谢谢。

posted @ 2019-05-11 17:33  蓝银杏-SSW  阅读(197)  评论(0编辑  收藏  举报
//结束