题解——草莓

分草莓

自述:判断放进了循环,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 ;
}
相信下一次有人看这篇博客也是我退役的时候了。

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

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

猜你喜欢

转载自www.cnblogs.com/ssw02/p/10849347.html