767-C. Garland
2021-03-28 17:46:30 +0800 CST
Tags: tree
出處:https://codeforces.com/contest/767/problem/C
提交:https://codeforces.com/contest/767/submission/111229792
問題:給定 $n\in[3,10^6]$ 個有根樹的節點號碼 $[1,n]$、連接狀況 (父節點是誰,定義樹根的父節點為 0),以及各自的溫度 $[-100,100]$,現在想請問有沒有辦法三等份這整棵樹的溫度總和,使得每一棵子樹的溫度總和都是全部的三分之一。若無法則輸出 -1,若可行則輸出兩刀的子節點號碼。
解法:這題的作法其實可以意外地簡單,輸入資料的時候可以先算出整棵樹的溫度總和,然後就會有三分之一的值,接著一樣就是跑 DFS 回傳每個子樹 (延伸到樹葉) 的溫度和,一旦偵測到三分之一的話就把子樹的根記錄下來,然後此時的總和改成回傳 0 ,接著繼續找下一個三分之一的子樹,只要找滿兩刀就確定可以把這整棵樹三等分。實作細節的話要記得因為每一刀砍下來的子樹都不可能包含根,所以 DFS 要分成兩套,根節點用一套 dfs、其餘的非根節點用另外一套 dfs2。
心得:這題剛開始我是參考官方解答,分別找三分之一和三分之二的頂點,如果這兩類的頂點在同一棵子樹就是答案,否則就要找兩個不屬於同一棵樹的三分之一頂點作為解答。但是這樣實作起來非常困難,因為我們無法隨意地判斷兩個頂點是否屬於同一棵子樹,而且此作法無法推廣成任意 k 等分,所以多多參考神人的解答總是沒有壞處的。
延伸:根據此題解法易知,只要找到 k-1 刀底下的子樹總和都各自為 sum / k,就能確保此大樹可以切為 k 等分。
實作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <bits/stdc++.h>
using namespace std ;
int n , index1 , index2 , sum ;
vector < int > adj [ 1000001 ]; short t [ 1000001 ];
int dfs2 ( int u ) {
int ans = t [ u ];
for ( auto v : adj [ u ]) ans += dfs2 ( v );
if ( ! index1 && ans * 3 == sum ) {
index1 = u ;
return 0 ; // very smart technique!
}
else if ( index1 && ans * 3 == sum ) index2 = u ;
return ans ;
}
void dfs ( int u ) {
for ( auto v : adj [ u ]) {
dfs2 ( v );
if ( index1 && index2 ) break ;
}
}
int main () { ios_base :: sync_with_stdio ( false ); cin . tie ( nullptr ); // IO 優化
cin >> n ;
for ( int i = 1 ; i <= n ; i ++ ) {
int a ; cin >> a >> t [ i ]; sum += t [ i ];
adj [ a ]. push_back ( i );
}
if ( sum % 3 != 0 ) cout << - 1 ;
else {
dfs ( adj [ 0 ][ 0 ]);
if ( index1 && index2 ) cout << index1 << " " << index2 ;
else cout << - 1 ;
}
return 0 ;
}
結果:
Please enable JavaScript to view the comments powered by Disqus.