CF EDU 127 E - Preorder
阿新 • • 發佈:2022-05-12
E - Preorder
樹形dp
若兩顆滿二叉樹深度相同,一顆二叉樹交換任意兩個兄弟結點及其子樹可以得到另一顆二叉樹,則成這兩顆二叉樹同構
設 u 的左右兒子為 ls,rs
- 若 ls 與 rs 同構,則 \(ans_u=ans_{ls}*ans_{rs}\)
- 否則,ls,rs 交換又是一種新方案,\(ans_u=ans_{ls}*ans_{rs}*2\)
關鍵為如何判斷兩顆二叉樹同構
性質:設二叉樹有 n 個結點,每個結點的子樹大小之和是 nlogn 級別
證明:設深度為 h,\(1*(2^h-1)+2*(2^{h-1}-1)+4*(2^{h-2}-1)+...+2^{h-1}*(2-1)=h*2^h-2^h+1\) ,
因此約為 nlogn
所以可以求出所有結點的子樹的字串,即\(s[u]=str[u]+s[ls]+s[rs]\)並規定左子樹字典序 < 右子樹,這樣直接看兩個子樹對應的字串是否相等就可以判斷是否同構
求結點深度時如果用 log 函式,注意精度誤差
#include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <cmath> #include <map> #define ls u << 1 #define rs u << 1 | 1 #define log2(x) (log(x) / log(2)) using namespace std; typedef long long ll; typedef pair<int, int> PII; const int mod = 998244353; const int N = 1 << 19; const double eps = 1e-8; int n; string str, s[N]; ll f[N]; //給出結點編號求其在完全二叉樹上的編號 int get_h(int u) { double ans = log2(u + 1); //避免精度誤差 if (abs(ans - round(ans)) < eps) return ans; return ceil(ans); } ll dfs(int u) { //判斷是否為葉子結點 if (get_h(u) == n) // if((u << 1) >= (1 << n)) 更好 { s[u] = str[u]; return 1; } if (f[u]) return f[u]; ll ans = dfs(ls) * dfs(rs) % mod; if (s[ls] != s[rs]) ans = ans * 2 % mod; if (s[ls] > s[rs]) swap(s[ls], s[rs]); s[u] = str[u] + s[ls] + s[rs]; return f[u] = ans; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); cin >> n >> str; str = " " + str; cout << dfs(1) << endl; return 0; }