CF EDU 127 E - Preorder
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;
}