Codeforces Round #556 (Div. 2) E. Tree Generator™(线段树教做人系列)

题目链接:https://codeforces.com/contest/1150/problem/E

题意:现在有一个括号序列,括号序列表达的是一个先序遍历的树结构,左括号表示从当前节点向下遍历,右括号表示向上回溯。有 m m m个操作,每次操作交换两个括号的位置,保证交换之后括号的匹配同样合理。现在你需要输出当前括号序列表示的树直径。

解题心得:

  • 假设将树用树链剖分标号,那么两个被标号的节点 a a a b b b,如果 a &lt; b a&lt;b a<b那么他们的 l c m lcm lcm一定在 a a a b b b之间,并且 l c m lcm lcm一定是 a a a b b b之间深度最小的那个节点。

  • 这样一个树的直径就是遍历 a a a b b b d e p t h [ a ] + d e p t h [ b ] − 2 ∗ d e p t h [ l c m ] depth[a] + depth[b] - 2*depth[lcm] depth[a]+depth[b]2depth[lcm]的最大值。但是如果在一棵树上按照公式找直径时间复杂度是非常大的,并且每次交换括号之后树都会变形,所以不能将括号序列化成树来找直径。

  • 其实仔细观察会发现我们只需要每个节点的深度就行了,而深度可以直接由括号序列得到,假设左括号是 1 1 1,右括号是 − 1 -1 1,那么某段括号区间代表的相对深度就能直接得到。这样就可以直接使用线段树进行维护,需要维护的值比较多,包括每段区间的:

    • M a x Max Max_ d e p t h depth depth: 当前区间最深节点的深度—— d e p t h [ a ] depth[a] depth[a]
    • M i n Min Min_ d e p t h depth depth: 当前区间最浅节点的深度—— d e p t h [ l c m ] depth[lcm] depth[lcm]
    • M a x Max Max_ l v lv lv:当前区间左儿子最大深度减去右儿子最浅深度×2—— d e p t h [ a ] − 2 ∗ d e p t h [ l c m ] depth[a]-2*depth[lcm] depth[a]2depth[lcm]
    • M a x Max Max_ r v rv rv:当前区间右儿子最大深度减去左儿子最浅深度×2—— d e p t h [ b ] − 2 ∗ d e p t h [ l c m ] depth[b]-2*depth[lcm] depth[b]2depth[lcm]
    • M a x Max Max_ l r v lrv lrv:当前区间的直径(要注意这个并不是直接简单的将上面两个加起来就完事,需要判断是从 M a x Max Max_ l v lv lv+右儿子最大节点深度还是从 M a x Max Max_ r v rv rv+左儿子最大节点深度得到)—— d e p t h [ a ] − 2 ∗ d e p t h [ l c m ] + d e p t h [ b ] depth[a]-2*depth[lcm]+depth[b] depth[a]2depth[lcm]+depth[b]

codeforces原生的题解:https://codeforces.com/blog/entry/66783



#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;

struct Node {
    int depth, Min_depth, Max_depth, Max_lv, Max_rv, Max_lrv;
}node[maxn<<2];

int n, m;
char parenthesis[maxn];

void init() {
    scanf("%d%d%s", &n, &m, parenthesis+1);
}

Node get_left_parenthesis() {
   return Node{1, 0, 1, 0, 1, 1};
}

Node get_right_parenthesis() {
    return Node{-1, -1, 0, 2, 1, 1};
}

Node shifted_left(int depth, int root) {
    return Node{node[root].depth + depth, node[root].Min_depth+depth, node[root].Max_depth+depth,
                node[root].Max_lv-depth, node[root].Max_rv-depth, node[root].Max_lrv};
}

void merge(int root) {
    int chl = root<<1;
    int chr = root<<1|1;

    Node rhs_shifted = shifted_left(node[chl].depth, chr);

    node[root].depth = rhs_shifted.depth;
    node[root].Min_depth = min(node[chl].Min_depth, rhs_shifted.Min_depth);
    node[root].Max_depth = max(node[chl].Max_depth, rhs_shifted.Max_depth);
    node[root].Max_lv = max(node[chl].Max_lv, max(rhs_shifted.Max_lv, node[chl].Max_depth - 2*rhs_shifted.Min_depth));
    node[root].Max_rv = max(node[chl].Max_rv, max(rhs_shifted.Max_rv, rhs_shifted.Max_depth - 2*node[chl].Min_depth));
    node[root].Max_lrv = max(max(node[chl].Max_lrv, rhs_shifted.Max_lrv), max(node[chl].Max_lv + rhs_shifted.Max_depth,
                                                                              rhs_shifted.Max_rv + node[chl].Max_depth));
}

void build_tree(int root, int l, int r) {
    if(l == r) {
        if(parenthesis[l] == '(') node[root] = get_left_parenthesis();
        else node[root] = get_right_parenthesis();
        return ;
    }

    int mid = l + r >> 1;
    build_tree(root<<1, l, mid);
    build_tree(root<<1|1, mid+1, r);

    merge(root);
}

int query_max() {
    return node[1].Max_lrv;
}

void change(int root, int l, int r, int find_pos, int change_pos) {
    if(l == r) {
        if(parenthesis[change_pos] == '(') node[root] = get_left_parenthesis();
        else node[root] = get_right_parenthesis();
        return ;
    }

    int mid = l + r >> 1;
    if(find_pos <= mid) {
        change(root<<1, l, mid, find_pos, change_pos);
    } else {
        change(root<<1|1, mid+1, r, find_pos, change_pos);
    }

    merge(root);
}

void change(int pos1, int pos2) {
    change(1, 1, 2*n-2, pos1, pos2);
    change(1, 1, 2*n-2, pos2, pos1);
}

int main() {
//    freopen("1.in", "r", stdin);
    init();
    build_tree(1, 1, 2*n-2);

    printf("%d\n", query_max());
    while(m--) {
        int pos1, pos2; scanf("%d%d",&pos1, &pos2);
        change(pos1, pos2);
        swap(parenthesis[pos1], parenthesis[pos2]);
        printf("%d\n", query_max());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值