题目:http://acm.hdu.edu.cn/showproblem.php?pid=6161
题意:给你一颗二叉树,每个节点的值为自身,接下来有2种操作
query:查询经过该点的路径的最大值
change:将一个点的值改变
官方题解:
考虑dp,f(x)表示从点x开始向下走得到的最大的点权和,查询直接从x开始向上走更新答案即可。
考虑快速算 f(x) 对于子树内没有被修改过的点的 f(x) 可以快速分类讨论算出,而不满足本条件的点只有 O(mlogm) 个,在hash上dp即可。
考虑快速算 f(x) 对于子树内没有被修改过的点的 f(x) 可以快速分类讨论算出,而不满足本条件的点只有 O(mlogm) 个,在hash上dp即可。
f(x)用贪心进行计算,先看左右儿子的深度,如果相同则选择右儿子走到底,否则最后应该选择n为最后的节点
查询时需要枚举所有情况
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<stack> #include<map> #include<set> using namespace std; #define l(X) (X<<1) #define r(X) (X<<1|1) typedef long long ll; int n; map<int,ll>f,val; ll cal(int x) { if (x>n) return 0; if (f.count(x)) return f[x]; int t=x,sl=0,sr=0; while(l(t)<=n) { sl++; t=l(t); } t=x; while(r(t)<=n) { sr++; t=r(t); } if (sl!=sr) t=n; ll ans=0; while(t>=x) { ans+=t; t>>=1; } return ans; } void update(int x,int y) { val[x]=y; while(x) { f[x]=max(cal(l(x)),cal(r(x))) +(val.count(x)?val[x]:x); x>>=1; } } ll query(int x) { ll ans=cal(l(x))+cal(r(x))+(val.count(x)?val[x]:x); ll now=cal(x); while(x>>1) { bool flag=x&1; x>>=1; now+=(val.count(x)?val[x]:x); if (flag) ans=max(ans,now+cal(l(x))); else ans=max(ans,now+cal(r(x))); } return ans; } int main() { char s[10]; int m; while(scanf("%d%d",&n,&m)!=EOF) { f.clear();val.clear(); int x,y; while(m--) { scanf("%s",s); if (s[0]=='q') { scanf("%d",&x); printf("%lld ",query(x)); } else { scanf("%d%d",&x,&y); update(x,y); } } } return 0; }