CF EDU 127 E - Preorder

E - Preorder

树形dp

若两颗满二叉树深度相同,一颗二叉树交换任意两个兄弟结点及其子树可以得到另一颗二叉树,则成这两颗二叉树同构

设 u 的左右儿子为 ls,rs

  1. 若 ls 与 rs 同构,则 ansu=anslsansrs
  2. 否则,ls,rs 交换又是一种新方案,ansu=anslsansrs2

关键为如何判断两颗二叉树同构

性质:设二叉树有 n 个结点,每个结点的子树大小之和是 nlogn 级别

证明:设深度为 h,1(2h1)+2(2h11)+4(2h21)+...+2h1(21)=h2h2h+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;
}
posted @   hzy0227  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示