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

来源:互联网 发布:sftp默认端口号 编辑:程序博客网 时间:2024/06/05 09:08

题目链接

题意

有一棵 nn1e8)个节点的完全二叉树,节点 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;}
原创粉丝点击