题目链接:点击打开链接
题解思路:求通过此点求最大路径和无非两种:1.路径通过节点的两个儿子2.路径通过节点的一个儿子和自己父节点的另一个儿子,那么我们用dp[i]表示节点i通过他的一个儿子到叶子节点最大和,那么就可以递推更新答案了。
代码:
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
const int mx = 1e5+10;
int n,m;
char str[20];
map<int,ll>val,dp;
ll cal(ll x){
if(x>n) return 0;
if(dp.count(x)) return dp[x];
ll lson = 0,rson = 0,ts;
ts = x;
while((ts<<1)<=n){
ts <<= 1;
lson++;
}
ts = x;
while((ts<<1|1)<=n){
ts = (ts<<1|1);
rson++;
}
if(rson!=lson) ts = n;
ll ret = 0;
while(ts>=x){
ret += (val.count(ts)?val[ts]:ts);
ts >>= 1;
}
return ret;
}
void update(ll x,ll v){
val[x] = v;
while(x){
dp[x] = max(cal(x<<1),cal(x<<1|1))+(val.count(x)?val[x]:x);
x >>= 1;
}
}
ll query(ll x){
ll ans = cal(x<<1)+cal(x<<1|1)+(val.count(x)?val[x]:x);
ll ret = cal(x);
while((x>>1)){
int flag = (x%2);
x >>= 1;
ret += (val.count(x)?val[x]:x);
if(flag) ans = max(ans,ret + cal(x<<1));
else ans = max(ans,ret + cal(x<<1|1));
}
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m)){
ll x , y;
val.clear(); dp.clear();
while(m--){
scanf("%s",str);
if(str[0]=='q'){ scanf("%lld",&x); printf("%lld\n",query(x)); }
else { scanf("%lld%lld",&x,&y); update(x,y); }
}
}
return 0;
}