又是一道非常水的普及 T4,直接按照题目意思算就好了。

题意解读

给你一个二叉树,让你找其中最大的对称二叉树的节点数。
对称二叉树,简单来说就是以中间一条线轴对称的二叉树。

思路

首先非常好想,暴力遍历正个数,以每个节点为根,和其所有后代构成的二叉树,然后判断这棵树是否是对称二叉树,算出其节点数。最后找到最大的输出就好了。

时间复杂度分析

一眼 $ O(n^2) $,抱着必死的想法测试大样例,跑了 2s 多一点。我一看,没想到这机子跑这么快啊,然后加了个输入输出优化,没想跑到了 0.4s 的优秀成绩……

我当时就傻眼了。什么时候 $ O(n^2) $ 能跑过 1e6 了。这怕不会是老师把机子换成了 神威·太湖之光 吧……

后来,经过思考,发现:

虽然看起来是 $ O(n^2) $, 但祂实际上是 $O(nlogn)$的。

对于一棵满二叉树,我们从每个节点出发遍历这个结点的子树。

对于第一层的结点,子树大小为 $O(n)$,只有一个结点,所以时间是$O(n)$。

对于第二层的结点,子树大小 $O(\frac{n}{2})$,有两个结点,时间开销仍然是 $O(\frac{n}{2}) * 2 = O(n)$。

类似地,从任何一层出发遍历的时间都是 $O(n)$。

而树一共只有 $O(logn)$ 层。所以总时间复杂度是 $O(n) * O(logn) = O(nlogn)$

如果不是满二叉树,会因为不对称更早退出搜索,时间开销更低。

AC 记录

洛谷神鸡最慢才 176ms, 比 400ms 可快多了。

所以得出不是这机子跑得快,相比与洛谷神鸡,完全就是个垃圾

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, v[N], l[N], r[N], maxans;
bool check(int x, int y) // 判断两个数翻折后是否相同
{
if (1ll * x * y < 0) // 两个节点的结构一样
return 0;
if (x == -1) // 两个都为空
return 1;
if (v[x] != v[y]) // 两个权值一样
return 0;
return check(l[x], r[y]) && check(r[x], l[y]); // 搜左边的左子树和右边的右子树,与左边的右子树和右边的左子树都是否相同
}
int find(int x) // 计算节点数,其实可以放在check里,但我当时懒得再改了
{
int ans = 1;
if (l[x] != -1)
ans += find(l[x]);
if (r[x] != -1)
ans += find(r[x]);
return ans;
}
void dfs(int x) // 暴搜所有节点
{
if (check(l[x], r[x]))
maxans = max(maxans, find(x));
if (l[x] != -1)
dfs(l[x]);
if (r[x] != -1)
dfs(r[x]);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0); // 输入输出优化
cin >> n;
for (int i = 1; i <= n; i++)
cin >> v[i];
for (int i = 1; i <= n; i++)
cin >> l[i] >> r[i];
dfs(1);
cout << maxans;
}

暴力出奇迹!!!