题解 P5018 【对称二叉树】

· · 题解

广告:食用blog体验更佳

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

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

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

首先我们可以像树上莫队一样按照 左-右-根 的顺序将这棵树的欧拉序跑下来, 记下开始访问点xdfsL[x],和回溯时的dfsR[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]); 

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

记下开始访问点xdfsrL[x],和回溯时的dfsrR[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; } 

然后我们要怎么判断呢? 先判断左右儿子lsrssize是否相等

然后再判断第一遍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; 
}