【LG5018】[NOIP2018pj]对称的二叉树

题面

洛谷

题解

看到这一题全都是用\(O(nlogn)\)的算法过的

考场上写\(O(n)\)算法的我很不开心

然后就发了此篇题解。。。

首先我们可以像树上莫队一样按照 左-右-根 的顺序将这棵树的欧拉序跑下来,

记下开始访问点\(x\)的\(dfs\)序\(L[x]\),和回溯时的\(dfs\)序\(R[x]\)

再将记录欧拉序的数组记为\(P\)

void dfs(int x) {
P[L[x] = ++cnt] = x;
if (t[x].ch[0]) dfs(t[x].ch[0]);
if (t[x].ch[1]) dfs(t[x].ch[1]);
P[R[x] = ++cnt] = x;
t[x].size = t[t[x].ch[0]].size + t[t[x].ch[1]].size + 1;
}

统计出数组\(P\)的两个哈希值,一个是记录点权(\(hs1[0][x]\)),

另一个是记录当前点是左儿子还是右儿子(\(hs2[0][x]\))

for (int i = 1; i <= cnt; i++) hs1[0][i] = hs1[0][i - 1] * base + t[P[i]].v;
for (int i = 1; i <= cnt; i++) hs2[0][i] = hs2[0][i - 1] * base + get(P[i]);

再将这棵树按照 右-左-根 的顺序将这棵树的另一个欧拉序跑下来(记得清空),

记下开始访问点\(x\)的\(dfs\)序\(rL[x]\),和回溯时的\(dfs\)序\(rR[x]\)

void rdfs(int x) {
P[rL[x] = ++cnt] = x;
if (t[x].ch[1]) rdfs(t[x].ch[1]);
if (t[x].ch[0]) rdfs(t[x].ch[0]);
P[rR[x] = ++cnt] = x;
}

再记录一次统计出数组\(P\)的两个哈希值,一个是记录点权(\(hs1[1][x]\)),

另一个是记录当前点是左儿子还是右儿子(\(hs2[1][x]\))(这时候要取异或一下)

    for (int i = 1; i <= cnt; i++) hs1[1][i] = hs1[1][i - 1] * base + t[P[i]].v;
for (int i = 1; i <= cnt; i++) hs2[1][i] = hs2[1][i - 1] * base + (get(P[i]) ^ 1);

其中\(get\)函数:

inline int get(int x) { return t[t[x].fa].ch[1] == x; }

然后我们要怎么判断呢?

先判断左右儿子\(ls\)和\(rs\)的\(size\)是否相等

然后再判断第一遍\(dfs\)左儿子所覆盖的欧拉序内和

第二遍\(dfs\)右儿子所覆盖的欧拉序内两个哈希值相不相等即可

if (getHash(hs1[0], L[ls], R[ls]) != getHash(hs1[1], rL[rs], rR[rs])) continue;
if (getHash(hs2[0], L[ls], R[ls]) != getHash(hs2[1], rL[rs], rR[rs])) continue;

然而常数过大,速度被nlogn吊打

完整代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
namespace IO {
const int BUFSIZE = 1 << 20;
char ibuf[BUFSIZE], *is = ibuf, *it = ibuf;
inline char gc() {
if (is == it) it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin);
return *is++;
}
}
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (ch != '-' && (ch > '9' || ch < '0')) ch = IO::gc();
if (ch == '-') w = -1 , ch = IO::gc();
while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = IO::gc();
return w * data;
}
#define MAX_N 1000005
struct Node { int ch[2], fa, size, v; } t[MAX_N];
inline int get(int x) { return t[t[x].fa].ch[1] == x; }
typedef unsigned long long ull;
const ull base = 100007;
ull pw[MAX_N << 1];
ull hs1[2][MAX_N << 1], hs2[2][MAX_N << 1];
ull getHash(ull *hs, int l, int r) { return hs[r] - hs[l - 1] * pw[r - l + 1]; }
int N, L[MAX_N], R[MAX_N], rL[MAX_N], rR[MAX_N], P[MAX_N << 1], cnt;
void dfs(int x) {
P[L[x] = ++cnt] = x;
if (t[x].ch[0]) dfs(t[x].ch[0]);
if (t[x].ch[1]) dfs(t[x].ch[1]);
P[R[x] = ++cnt] = x;
t[x].size = t[t[x].ch[0]].size + t[t[x].ch[1]].size + 1;
}
void rdfs(int x) {
P[rL[x] = ++cnt] = x;
if (t[x].ch[1]) rdfs(t[x].ch[1]);
if (t[x].ch[0]) rdfs(t[x].ch[0]);
P[rR[x] = ++cnt] = x;
}
int main () {
N = gi(); pw[0] = 1;
for (int i = 1; i <= 2 * N; i++) pw[i] = pw[i - 1] * base;
for (int i = 1; i <= N; i++) t[i].v = gi();
for (int x = 1; x <= N; x++) {
int ls = gi(), rs = gi();
if (ls != -1) t[x].ch[0] = ls, t[ls].fa = x;
if (rs != -1) t[x].ch[1] = rs, t[rs].fa = x;
}
dfs(1);
for (int i = 1; i <= cnt; i++) hs1[0][i] = hs1[0][i - 1] * base + t[P[i]].v;
for (int i = 1; i <= cnt; i++) hs2[0][i] = hs2[0][i - 1] * base + get(P[i]);
cnt = 0; rdfs(1);
for (int i = 1; i <= cnt; i++) hs1[1][i] = hs1[1][i - 1] * base + t[P[i]].v;
for (int i = 1; i <= cnt; i++) hs2[1][i] = hs2[1][i - 1] * base + (get(P[i]) ^ 1);
int ans = 1;
for (int x = 1; x <= N; x++) {
int ls = t[x].ch[0], rs = t[x].ch[1];
if (t[ls].size != t[rs].size) continue;
if (getHash(hs1[0], L[ls], R[ls]) != getHash(hs1[1], rL[rs], rR[rs])) continue;
if (getHash(hs2[0], L[ls], R[ls]) != getHash(hs2[1], rL[rs], rR[rs])) continue;
ans = max(ans, t[x].size);
}
printf("%d\n", ans);
return 0;
}

【LG5018】[NOIP2018pj]对称的二叉树的更多相关文章

  1. [NOIP2018PJ]对称二叉树

    [NOIP2018PJ]对称二叉树 这个题正常人看到题面难道不是哈希? 乱写了个树哈希... #include<bits/stdc++.h> using namespace std; co ...

  2. 《剑指offer》第二十八题(对称的二叉树)

    // 面试题28:对称的二叉树 // 题目:请实现一个函数,用来判断一棵二叉树是不是对称的.如果一棵二叉树和 // 它的镜像一样,那么它是对称的. #include <iostream> ...

  3. (剑指Offer)面试题59:对称的二叉树

    题目: 请实现一个函数,用来判断一颗二叉树是不是对称的. 注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的. 思路: 对于一棵二叉树,从根结点开始遍历, 如果左右子结点有一个为NULL,那 ...

  4. 【剑指offer】面试题 28. 对称的二叉树

    面试题 28. 对称的二叉树 题目描述 题目:请实现一个函数,用来判断一颗二叉树是不是对称的.注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的. 解答过程 给定一个二叉树,检查它是否是镜像 ...

  5. 第28题:leetcode101:Symmetric Tree对称的二叉树

    给定一个二叉树,检查它是否是镜像对称的. 例如,二叉树 [1,2,2,3,4,4,3] 是对称的. 1 / \ 2 2 / \ / \ 3 4 4 3 但是下面这个 [1,2,2,null,3,nul ...

  6. 剑指Offer:对称的二叉树【28】

    剑指Offer:对称的二叉树[28] 题目描述 请实现一个函数,用来判断一颗二叉树是不是对称的.注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的. 题目分析 Java题解 /* publi ...

  7. 【Offer】[28] 【对称的二叉树】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 请实现一个函数,用来判断一-棵二叉树是不是对称的.如果一棵二叉树和它的镜像一样,那么它是对称的.  牛客网刷题地址 思路分析 利用前序 ...

  8. php算法题---对称的二叉树

    php算法题---对称的二叉树 一.总结 一句话总结: 可以在isSymmetrical()的基础上再加一个函数comRoot,函数comRoot来做树的递归判断 /*思路:首先根节点以及其左右子树, ...

  9. Leetcode:面试题28. 对称的二叉树

    Leetcode:面试题28. 对称的二叉树 Leetcode:面试题28. 对称的二叉树 Talk is cheap . Show me the code . /** * Definition fo ...

随机推荐

  1. Eclipse 连接真实机器调试

    一.手机开启调试模式 二.安装adb.exe 1.确信 \android-sdk-windows\tools\下有 adb.exe     AdbWinApi.dll     AdbWinUsbApi ...

  2. 【OpenCV】三种方式操作图像像素

    OpenCV中,有3种访问每个像素的方法:使用at方法.使用迭代器方法.使用指针 运行如下程序后可以发现使用at方法速度最快. 代码如下: //操作图像像素 #include <opencv2/ ...

  3. Codeforces Round #441 (Div. 2)【A、B、C、D】

    Codeforces Round #441 (Div. 2) codeforces 876 A. Trip For Meal(水题) 题意:R.O.E三点互连,给出任意两点间距离,你在R点,每次只能去 ...

  4. The Struts dispatcher cannot be found. This is usually caused by using Struts

    对于struts2中的问题: org.apache.jasper.JasperException: The Struts dispatcher cannot be found. This is usu ...

  5. Mysql分区表及自动创建分区Partition

    Range分区表建表语句如下,其中分区键必须和id构成主键和唯一键 CREATE TABLE `test1` ( `id` char(32) COLLATE utf8mb4_unicode_ci NO ...

  6. `ECS弹性计算服务

    云服务器(Elastic Compute Service 简称ECS)是一种简单高效,处理能力可弹性伸缩的计算服务.能快速构建更稳定.安全的应用,提升运维效率,降低IT成本. 云服务器ecs作用如下: ...

  7. 崩溃!UIAlertController 引起的崩溃

    UIAlertController 使用方法很简单,下面贴简单的使用方法: UIAlertController *alert = [UIAlertController alertControllerW ...

  8. 调试libRTMP代码来分析RTMP协议

    RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写.该协议基于TCP,是一个协议族,常用在视频直播领域.RTMP协议的默认端口是1935. 学习一个协议 ...

  9. linux 进程间通信方式

    管道: 它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信消息队列: 用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中 ...

  10. TestNG+Maven+IDEA 自动化测试(一) 环境搭建

    示例代码: https://github.com/ryan255/TestNG-Demo 所需环境: 1. IDEA UItimate 2. JDK 3. Maven 创建工程 一开始创建一个普通的m ...