1. 程式人生 > >【NOIP2018普及組】對稱二叉樹

【NOIP2018普及組】對稱二叉樹

@對稱二叉樹@


@題目描述@

一棵有點權的有根樹如果滿足以下條件,則被軒軒稱為對稱二叉樹:

  1. 二叉樹;
  2. 將這棵樹所有節點的左右子樹交換,新樹和原樹對應位置的結構相同且點權相等。

下圖中節點內的數字為權值,節點外的 id 表示節點編號。
題目描述圖

現在給出一棵二叉樹,希望你找出它的一棵子樹,該子樹為對稱二叉樹,且節點數最多。請輸出這棵子樹的節點數。
注意:只有樹根的樹也是對稱二叉樹。本題中約定,以節點 T 為子樹根的一棵“子樹”指的是:節點 T 和它的全部後代節點構成的二叉樹。

輸入
第一行一個正整數 n,表示給定的樹的節點的數目,規定節點編號 1~n,其中節點1 是樹根。
第二行 n 個正整數,用一個空格分隔,第 i 個正整數 vi 代表節點 i 的權值。
接下來 n 行,每行兩個正整數 li , ri ,分別表示節點 i 的左右孩子的編號。如果不存在左 / 右孩子,則以 −1 表示。兩個數之間用一個空格隔開。
輸出
輸出共一行,包含一個整數,表示給定的樹的最大對稱二叉子樹的節點數。

輸入樣例 1
2
1 3
2 -1
-1 -1
輸入樣例 2
10
2 2 5 5 5 5 4 4 2 3
9 10
-1 -1
-1 -1
-1 -1
-1 -1
-1 2
3 4
5 6
-1 -1
7 8

輸出樣例 1
1
輸出樣例 2
3

輸入輸出樣例 1 說明
樣例解釋1
最大的對稱二叉子樹為以節點 2 為樹根的子樹,節點數為 1。

輸入輸出樣例 2 說明
樣例解釋2
最大的對稱二叉子樹為以節點 7 為樹根的子樹,節點數為 3。

資料規模與約定
共25個測試點。vi≤1000
測試點1~3,n≤10,保證根結點的左子樹的所有節點都沒有右孩子,根結點的右子樹的所有節點都沒有左孩子。
測試點4~8,n≤10。
測試點9~12,n≤10^5,保證輸入是一棵“滿二叉樹”。
測試點13~16,n≤10^5,保證輸入是一棵“完全二叉樹”。
測試點17~20,n≤10^5,保證輸入的樹的點權均為 1。
測試點21~25,n≤10^6。

@題解@

一開始看他們傳什麼馬拉車演算法心中一驚,以為今年普及組要上天。後來說是樹雜湊,要靠譜點,也差不多是普及組 T4 的水平。
結果……今年普及組真的是要上天……只不過是反向上天……

這道題做法很暴力:列舉每個結點,如果它左右子樹大小相同,則暴力 Check 一下以這個結點為根的子樹是否合法。

雖然看上去很暴力……但是這樣搞的時間複雜度的確是O(nlog n)。
證明思路可以採用啟發式合併的時間複雜度證明思路。
即:因為左右子樹相同時才 check,因此每一次 check 樹的大小至少增大一倍。最多 log 次樹的大小就會到達 n,所以每個結點只會被 check log 次。

哎……心疼那些做 T3 結果沒做出來反而 T4 這麼簡單卻沒有分的同學們……

@程式碼@

#include<cstdio>
inline int max(int x, int y) {
	return x > y ? x : y;
}
const int MAXN = 1000000;
int siz[MAXN + 5], le[MAXN + 5], ri[MAXN + 5], v[MAXN + 5], ans;
bool check(int r1, int r2) {
	if( v[r1] != v[r2] ) return false;
	else if( r1 == 0 && r2 == 0 ) return true;
	else return check(le[r1], ri[r2]) && check(ri[r1], le[r2]);
}
int dfs1(int rt) {
	if( !rt ) return 0;
	else return siz[rt] = dfs1(le[rt]) + dfs1(ri[rt]) + 1;
}
void dfs2(int rt) {
	if( !rt ) return ;
	if( siz[le[rt]] == siz[ri[rt]] )
		if( check(le[rt], ri[rt]) ) ans = max(ans, siz[rt]);
	dfs2(le[rt]), dfs2(ri[rt]);
}
int main() {
	int n; v[0] = -1;
	scanf("%d", &n);
	for(int i=1;i<=n;i++)
		scanf("%d", &v[i]);
	for(int i=1;i<=n;i++) {
		scanf("%d%d", &le[i], &ri[i]);
		if( le[i] == -1 ) le[i] = 0;
		if( ri[i] == -1 ) ri[i] = 0;
	}
	dfs1(1), dfs2(1);
	printf("%d\n", ans);
}

@[email protected]

就是這樣,新的一天裡,也請多多關照哦(ノω<。)ノ))☆.。