2017多校九 01题 HDU6161 Big binary tree 树形dp+hash

34 篇文章 0 订阅
6 篇文章 0 订阅

题目链接

题意

有一棵 n n1e8)个节点的完全二叉树,节点 i 的父亲节点是 i2。初始时每个点的权值都是它本身。现有两种操作 m 次(m1e5):

  1. 修改某个点的权值

  2. 询问 经过某个点的 权值和最大的 链 的权值和

思路

dp[ ] 记录从某个点向下走最长的链的权值和,修改即一路向上更新 dp[ ] 值。

考虑经过点 u 的权值和最大的链,它必然是经过 lson(u) 的链,经过 rson(u) 的链,和经过 fa(u) 的链三者中最大的两个连接起来的。而计算经过 fa(u) 的链则可以一路向上算,想法类似 hdu 2196 Computer dfs / 树形dp.

但这道题的重点在于数据范围—— n1e8 . 因此不可能直接开个数组来存。于是考虑用 map ,并且只存因修改而受影响的节点的值。其他的用到再算,毕竟是完全二叉树,算一次的代价不会超过 O(logn) .

至于算,就是尽量一路向右走(因为一旦要算就说明这棵子树内的点都没有被修改过),特殊情况是如果 n 在这个点的子树内,那么要走经过 n 的那条链,都倒着处理即可。

Code

#include <bits/stdc++.h>
#define lson(x) (x << 1)
#define rson(x) (x << 1 | 1)
using namespace std;
typedef long long LL;
int n, m;
map<int, LL> dp, val;
LL calc(int u) {
    if (u > n) return 0;
    if (dp[u]) return dp[u];
    int temp = u, ldep = 0, rdep = 0;
    while (temp <= n) temp = lson(temp), ++ldep;
    temp = u;
    while (temp <= n) temp = rson(temp), ++rdep;
    if (ldep != rdep) temp = n;
    else temp /= 2;
    LL ret = 0;
    while (true) {
        ret += temp;
        if (temp == u) break;
        temp /= 2;
    }
    return ret;
}
LL uval, sum;
void dfs(int u, int son) {
    if (u == 0) return;
    uval = max(uval, sum + (val[u] ? val[u] : u) + calc(son ^ 1));
    if (u == 1) return;
    sum += val[u] ? val[u] : u;
    dfs(u/2, u);
}
LL ask(int u) {
    LL lval = calc(lson(u)), rval = calc(rson(u));
    uval = sum = 0;
    dfs(u/2, u);
    LL mn = min(min(lval, rval), uval);
    return lval + rval + uval - mn + (val[u] ? val[u] : u);
}
void update(int u) {
    if (u == 0) return;
    dp[u] = max(calc(lson(u)), calc(rson(u))) + (val[u]?val[u]:u);
    if (u == 1) return;
    update(u/2);
}
void change(int u, LL x) {
    dp[u] = (dp[u]?dp[u]:calc(u)) - (val[u]?val[u]:u) + x;
    val[u] = x;
    update(u/2);
}
void work() {
    val.clear();
    dp.clear();
    char s[10];
    while (m--) {
        scanf("%s", s);
        if (s[0] == 'q') {
            int u;
            scanf("%d", &u);
            printf("%lld\n", ask(u));
        }
        else {
            int u; LL x;
            scanf("%d%lld", &u, &x);
            change(u, x);
        }
    }
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) work();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值