题目: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\n",query(x));
}
else
{
scanf("%d%d",&x,&y);
update(x,y);
}
}
}
return 0;
}