Gym 100499E (树DP)

E. Binary Search Tree
time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

A binary tree is a tree data structure where each node has at most two children which are referred to as left child and right child. A binary search tree is a binary tree where each node has a comparable key and must satisfy the following conditions:

  • The left sub-tree of a node contains only nodes with keys less than the node’s key.
  • The right sub-tree of a node contains only nodes with keys more than the node’s key.
  • The left and the right sub-tree are also binary search tree.

Given a binary tree, your task is to choose a maximal set S of connected nodes in the given tree so that these nodes and the relations (left child, right child, or ancestor) among them form a binary search tree.

Input

The first line of input is the number T (T ≤ 20).

Then T tests follow:

  • The first line of each test is the number N (1 ≤ N ≤ 105) - the number of node of the given binary tree
  • Then N lines follow. The ith line describes node numbered i (nodes are numbered 1 to N). Each node is described by 3 integers: the index of its left child, the index of its right child, and the comparable key in that node. The value of a key will not exceed 106. If a node doesn’t have a left child and/or a right child, they will be represented as 0. The root node is numbered 1.
Output

For each test, print the maximal size of S.

Sample test(s)
input
2
8
2 3 10
4 5 5
6 7 15
0 0 3
0 0 7
0 0 17
0 8 13
0 0 14
3
0 2 5
0 3 6
0 0 7
output
5
3
Note

The example test case correspond to the following figure:


题意是给定一棵二叉树,问这个二叉树最大能构成几个节点的二叉索引树。

一开始想简单了,以为只要左儿子的权值小于父节点就能添加整棵左子树。其实左儿子的右


子树中有可能存在比父节点权值大的节点,我们需要把以这个节点为根的子树删除。查找这

样的矛盾节点可以通过暴力查找。

对于一个父亲节点,以他为根的子树,如果他的左子树中存在一个比他权值大的,只有可能

出现在他左子树的右路径上(比如样例1中对于10这个父亲节点他的左儿子的右路径就是

5->7),现在正确性有了保证我们需要证明复杂度。

这个方法的复杂度其实是线性的。对于某一个节点,除了第一次递归经过他以外,在使用上

述方法时最多经过2次,也就是这个点最多处一次左儿子的右路径或者右儿子的左路径,所以

这个算法的复杂度是O(n),优于题解的O(nlgn)。

<span style="font-size:12px;">#include <bits/stdc++.h>
using namespace std;
#define maxn 111111
#define maxm 211111
#define find Find

struct node {
    int from, to, next;
}edge1[maxm], edge2[maxn];
int head1[maxn], head2[maxn], a[maxn];
int cnt1, cnt2;
int n;
int b[maxn][3];
int degree[maxn]; //入度
int ans, dp[maxn];
bool vis[maxn];

void add_edge (int from, int to, int id) { //id=0表示左儿子 1表示右儿子
    if (id == 0) {
        edge1[cnt1].from = from, edge1[cnt1].to = to, edge1[cnt1].next = head1[from], head1[from] = cnt1++;
    }
    else if (id == 1) {
        edge2[cnt2].from = from, edge2[cnt2].to = to, edge2[cnt2].next = head2[from], head2[from] = cnt2++;
    }
}

int find (int u, int id, int val) { //id = 0 表示左路 1表示右路
    if (vis[u])
        return -1;
    if (id == 0) {
        int i = head1[u];
        if (i == -1)
            return -1;
        int lson = edge1[i].to;
        if (a[lson] < val) {
            vis[lson] = 1;
            return lson;
        }
        else
            return find (lson, id, val);
    }
    else if (id == 1) {
        int i = head2[u];
        if (i == -1)
            return -1;
        int rson = edge2[i].to;
        if (a[rson] > val) {
            vis[rson] = 1;
            return rson;
        }
        else
            return find (rson, id, val);
    }
}

void dfs (int u) { //cout << "u:" << u << endl;
    dp[u] = 1;
    int i = head1[u], j = head2[u];
    if (i != -1) {
        int lson = edge1[i].to;
        dfs (lson);
        if (a[u] > a[lson])
            dp[u] += dp[lson];
        else vis[lson] = 1;
        int bug = find (lson, 1, a[u]); //从左儿子的右路找到第一个比a[u]大的节点编号
        if (bug != -1) {
            dp[u] -= dp[bug];
        }
    }
    if (j != -1) {
        int rson = edge2[j].to;
        dfs (rson);
        if (a[u] < a[rson])
            dp[u] += dp[rson];
        else vis[rson] = 1;
        int bug = find (rson, 0, a[u]);
        if (bug != -1) {
            dp[u] -= dp[bug];
        }
    }
    ans = max (ans, dp[u]);
}

int main () {
    //freopen ("in", "r", stdin);
    int t;
    scanf ("%d", &t);
    while (t--) {
        scanf ("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf ("%d%d%d", &b[i][0], &b[i][1], &b[i][2]);
        }
        memset (degree, 0, sizeof degree);
        memset (head1, -1, sizeof head1);
        memset (head2, -1, sizeof head2);
        cnt1 = cnt2 = 0;
        for (int i = 1; i <= n; i++) {
            a[i] = b[i][2];
            if (b[i][0] != 0) {
                add_edge (i, b[i][0], 0);
                degree[b[i][0]]++;
            }
            if (b[i][1] != 0) {
                add_edge (i, b[i][1], 1);
                degree[b[i][1]]++;
            }
        }
        int root;
        for (int i = 1; i <= n; i++) {
            if (!degree[i]) {
                root = i;
                break;
            }
        }
        //cout << root << endl;
        ans = 0;
        memset (dp, 0, sizeof dp);
        memset (vis, 0, sizeof vis);
        dfs (root);
        cout << ans << endl;
    }
    return 0;
}</span>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值