zoukankan      html  css  js  c++  java
  • 题解——草莓(树上切割)

    分草莓(树上切割)

    自述:判断放进了循环,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
    ");
    	else printf("NO
    ");
    }
    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
    ") ; clear() ; continue ; }
    		dfs( 0 ) ;
    		Ans_check();
    		clear();
    	}
    	return  0 ;
    }
    
    相信下一次有人看这篇博客也是我退役的时候了。

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

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

  • 相关阅读:
    条款1:理解模板类型推导
    非受限联合体
    整型
    vector作为函数返回类型
    SQL Server数据库空间管理 (1)
    1085 PAT单位排行 (25 分
    1084 外观数列 (20 分)
    1083 是否存在相等的差 (20 分)
    1082 射击比赛 (20 分)
    1081 检查密码 (15 分)
  • 原文地址:https://www.cnblogs.com/ssw02/p/10849347.html
Copyright © 2011-2022 走看看