1. 程式人生 > >[BZOJ4027][HEOI2015]兔子與櫻花 樹形dp

[BZOJ4027][HEOI2015]兔子與櫻花 樹形dp

直接 大於 sample 葉子 esc long long \n tput input

Description

很久很久之前,森林裏住著一群兔子。有一天,兔子們突然決定要去看櫻花。兔子們所在森林裏的櫻花樹很特殊。櫻花樹由n個樹枝分叉點組成,編號從0到n-1,這n個分叉點由n-1個樹枝連接,我們可以把它看成一個有根樹結構,其中0號節點是根節點。這個樹的每個節點上都會有一些櫻花,其中第i個節點有c_i朵櫻花。櫻花樹的每一個節點都有最大的載重m,對於每一個節點i,它的兒子節點的個數和i節點上櫻花個數之和不能超過m,即son(i) + c_i <= m,其中son(i)表示i的兒子的個數,如果i為葉子節點,則son(i) = 0

現在兔子們覺得櫻花樹上節點太多,希望去掉一些節點。當一個節點被去掉之後,這個節點上的櫻花和它的兒子節點都被連到刪掉節點的父節點上。如果父節點也被刪除,那麽就會繼續向上連接,直到第一個沒有被刪除的節點為止。 現在兔子們希望計算在不違背最大載重的情況下,最多能刪除多少節點。 註意根節點不能被刪除,被刪除的節點不被計入載重。

Input

第一行輸入兩個正整數,n和m分別表示節點個數和最大載重

第二行n個整數c_i,表示第i個節點上的櫻花個數 接下來n行,每行第一個數k_i表示這個節點的兒子個數,接下來k_i個整數表示這個節點兒子的編號

Output

一行一個整數,表示最多能刪除多少節點。

Sample Input

10 4
0 2 2 2 4 1 0 4 1 1
3 6 2 3
1 9
1 8
1 1
0
0
2 7 4
0
1 5
0

Sample Output

4

HINT

對於100%的數據,1 <= n <= 2000000, 1 <= m <= 100000, 0 <= c_i <= 1000


數據保證初始時,每個節點櫻花數與兒子節點個數之和大於0且不超過m

Solution

做法:樹形$dp$

考慮對於一個以$u$為根節點的子樹,肯定從權重最小的子樹開始刪,所以用個$vector$存圖,直接$sort$...

我一開始還想著用個堆來維護這個$min$,然後發現直接$sort$就可以了..$vector$存圖並不需要先後..

#include <bits/stdc++.h>

using namespace std ;

#define N 4000100
#define ll long long

int n , m , c[ N ] , fa[ N ] , ans = 0
; vector <int> e[ N ] ; bool cmp( int a , int b ) { return c[ a ] < c[ b ] ; } void dfs( int u ) { for( int i = 0 , len = e[ u ].size() ; i < len ; i ++ ) { dfs( e[ u ][ i ] ) ; } sort( e[ u ].begin() , e[ u ].end() , cmp ) ; c[ u ] += e[ u ].size() ; for( int i = 0 , len = e[ u ].size() ; i < len ; i ++ ) { if( c[ e[ u ][ i ] ] + c[ u ] - 1 <= m ) { c[ u ] += c[ e[ u ][ i ] ] - 1 ; ans ++ ; } else break ; } } int main() { scanf( "%d%d" , &n , &m ) ; for( int i = 1 ; i <= n ; i ++ ) { scanf( "%d" , &c[ i ] ) ; } for( int i = 1 ; i <= n ; i ++ ) { int k , x ; scanf( "%d" , &k ) ; for( int j = 1 ; j <= k ; j ++ ) { scanf( "%d" , &x ) ; x ++ ; e[ i ].push_back( x ) ; } } dfs( 1 ) ; printf( "%d\n" , ans ) ; }

[BZOJ4027][HEOI2015]兔子與櫻花 樹形dp