题目链接
题意
有一棵
n
(
修改某个点的权值
询问 经过某个点的 权值和最大的 链 的权值和
思路
dp[ ] 记录从某个点向下走最长的链的权值和,修改即一路向上更新 dp[ ] 值。
考虑经过点
u
的权值和最大的链,它必然是经过
但这道题的重点在于数据范围—— n≤1e8 . 因此不可能直接开个数组来存。于是考虑用 map ,并且只存因修改而受影响的节点的值。其他的用到再算,毕竟是完全二叉树,算一次的代价不会超过 O(logn) .
至于算,就是尽量一路向右走(因为一旦要算就说明这棵子树内的点都没有被修改过),特殊情况是如果
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;
}