分析:很显然这个一个树,视作0为根节点。从0号节点到某一节点有且只有一条可达路径。那么每个节点i可以对应一个路径(从0到i这条路径)。对路径通过后序遍历重新编号为1~n.叶子节点所对应区间为[Ki,Kii].Ki为该节点对应的路径的编号。对于非叶子节点对应区间为[Min,Max],Min是该节点所有孩子节点区间左端点中最小的。Max肯定为自身(因为父节点的路径编号要大于孩子节点的路径编号)。那么这个区间的意义是? 如果一个节点C的区间为[1,3]那么1,2,3这三个编号对应的路径中都经过节点C。并且可以算出每个路径的值也就是说节点0到每个节点的路径长度.NUM[I] 0~i路径的长度。
那么查询操作求经过X节点的最大路径就是X对应的区间[ L[X],R[X] ]中的这些路径对应NUM值的最大值。注意:这里是找一个区间的最大值。
而修改操作让X节点的值改为Y,也就是将X对应区间[ L[X],R[X] ]中这些路径对应的每个NUM值都加上Y-V[X]。(V[X]为原本X节点的价值)。 注意:这里是修改一个区间的值。
所以我们可以看出来,我们可以先用dfs处理,然后用线段树去优化后面的操作。
代码:
#include
#include
#include
#include
#define ll long long
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
const int MAXN = 100100;
vector edge[MAXN];
int m,n;
struct treenode
{
int left,right,mid;
ll maxx,add;
}tree[MAXN<<4];
int L[MAXN],R[MAXN],index;
int w[MAXN];
ll num[MAXN];
void built(int l,int r,int rt)
{
tree[rt].left = l;
tree[rt].right = r;
tree[rt].mid = (l+r)>>1;
tree[rt].add = 0;
if(l == r)
{
tree[rt].maxx = num[l];
return;
}
built(l,tree[rt].mid,rt<<1);
built(tree[rt].mid+1,r,(rt<<1)|1);
tree[rt].maxx = max(tree[rt<<1].maxx,tree[(rt<<1)|1].maxx);
}
void update(int rt,int l,int r,int data)
{
if(tree[rt].left == l && r == tree[rt].right)
{
tree[rt].add += data;
return;
}
if(tree[rt].add != 0)
{
tree[rt<<1].add += tree[rt].add;
tree[(rt<<1)|1].add += tree[rt].add;
tree[rt].add = 0;
}
if(r <= tree[rt].mid)
{
update(rt<<1,l,r,data);
}
else if( l > tree[rt].mid)
{
update((rt<<1)|1,l,r,data);
}
else
{
update(rt<<1,l,tree[rt].mid,data);
update((rt<<1)|1,tree[rt].mid+1,r,data);
}
tree[rt].maxx = max(tree[rt<<1].maxx + tree[rt<<1].add,tree[(rt<<1)|1].maxx + tree[(rt<<1)|1].add);
}
ll query(int rt,int l,int r)
{
if(tree[rt].left == l && r == tree[rt].right )
{
return tree[rt].maxx + tree[rt].add;
}
if(tree[rt].add != 0)
{
tree[rt<<1].add += tree[rt].add;
tree[(rt<<1)|1].add += tree[rt].add;
tree[rt].add = 0;
}
ll ans;
if(r <= tree[rt].mid)
{
ans = query(rt<<1,l,r);
}
else if( l > tree[rt].mid)
{
ans = query((rt<<1)|1,l,r);
}
else
{
ans = max(query(rt<<1,l,tree[rt].mid),query((rt<<1)|1,tree[rt].mid+1,r));
}
tree[rt].maxx = max(tree[rt<<1].maxx + tree[rt<<1].add,tree[(rt<<1)|1].maxx + tree[(rt<<1)|1].add);
return ans;
}
void dfs(int now,int pre,ll sum)
{
sum += w[now];
int son = 0;
L[now] = MAXN;
for(int i = 0 ; i < edge[now].size() ; i ++)
{
int next = edge[now][i];
if(next == pre) continue;
son ++;
dfs(next,now,sum);
L[now] = min(L[now],L[next]);
}
R[now] = ++index;
num[index] = sum;
if(son == 0)
{
L[now] = R[now];
}
}
int main()
{
int t,Case = 0;
cin >> t;
while(t--)
{
printf("Case #%d:\n",++Case);
cin >> n >> m;
int u,v;
for(int i = 0 ; i <= n ; i ++) edge[i].clear();
for(int i = 0 ; i < n - 1 ; i ++)
{
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
for(int i = 0 ; i < n ; i ++)
scanf("%d",&w[i]);
index = 0;
dfs(0,0,0);
built(1,n,1);
int op;
for(int i = 0 ; i < m ; i ++)
{
scanf("%d",&op);
if(op == 0)
{
int x,y;
scanf("%d%d",&x,&y);
int data = y - w[x];
w[x] = y;
update(1,L[x],R[x],data);
}
else
{
int x;
scanf("%d",&x);
printf("%lld\n",query(1,L[x],R[x]));
}
}
}
return 0;
}
上面代码的线段树写略丑。应该是更新的姿势不太对。
下面这个线段树时间要比上面的快很多
#include
#include
#include
#include
#define ll long long
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
const int MAXN = 100100;
vector edge[MAXN];
int m,n;
struct treenode
{
int left,right,mid;
ll maxx,add;
}tree[MAXN<<4];
int L[MAXN],R[MAXN],index;
ll w[MAXN],num[MAXN];
void built(int l,int r,int rt)
{
tree[rt].left = l;
tree[rt].right = r;
tree[rt].mid = (l+r)>>1;
tree[rt].add = 0;
if(l == r)
{
tree[rt].maxx = num[l];
return;
}
built(l,tree[rt].mid,rt<<1);
built(tree[rt].mid+1,r,(rt<<1)|1);
tree[rt].maxx = max(tree[rt<<1].maxx,tree[(rt<<1)|1].maxx);
}
void update(int rt,int l,int r,ll data)
{
if(tree[rt].add != 0)
{
if(tree[rt].left != tree[rt].right)
{
tree[rt<<1].maxx += tree[rt].add;
tree[rt<<1].add += tree[rt].add;
tree[(rt<<1)|1].maxx += tree[rt].add;
tree[(rt<<1)|1].add += tree[rt].add;
tree[rt].add = 0;
}
}
if(tree[rt].left == l && r == tree[rt].right)
{
tree[rt].maxx += data;
tree[rt].add += data;
return;
}
if(r <= tree[rt].mid)
{
update(rt<<1,l,r,data);
}
else if( l > tree[rt].mid)
{
update((rt<<1)|1,l,r,data);
}
else
{
update(rt<<1,l,tree[rt].mid,data);
update((rt<<1)|1,tree[rt].mid+1,r,data);
}
tree[rt].maxx = max(tree[rt<<1].maxx,tree[(rt<<1)|1].maxx);
}
ll query(int rt,int l,int r)
{
if(tree[rt].add != 0)
{
if(tree[rt].left != tree[rt].right)
{
tree[rt<<1].maxx += tree[rt].add;
tree[rt<<1].add += tree[rt].add;
tree[(rt<<1)|1].maxx += tree[rt].add;
tree[(rt<<1)|1].add += tree[rt].add;
tree[rt].add = 0;
}
}
if(tree[rt].left == l && r == tree[rt].right )
{
return tree[rt].maxx;
}
if(r <= tree[rt].mid)
{
return query(rt<<1,l,r);
}
else if( l > tree[rt].mid)
{
return query((rt<<1)|1,l,r);
}
else
{
return max(query(rt<<1,l,tree[rt].mid),query((rt<<1)|1,tree[rt].mid+1,r));
}
}
void dfs(int now,int pre,ll sum)
{
sum += w[now];
int son = 0;
L[now] = MAXN;
for(int i = 0 ; i < edge[now].size() ; i ++)
{
int next = edge[now][i];
if(next == pre) continue;
son ++;
dfs(next,now,sum);
L[now] = min(L[now],L[next]);
}
R[now] = ++index;
num[index] = sum;
if(son == 0)
{
L[now] = R[now];
}
}
int main()
{
int t,Case = 0;
cin >> t;
while(t--)
{
printf("Case #%d:\n",++Case);
scanf("%d%d",&n,&m);
int u,v;
for(int i = 0 ; i <= n ; i ++) edge[i].clear();
for(int i = 0 ; i < n - 1 ; i ++)
{
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
for(int i = 0 ; i < n ; i ++)
scanf("%lld",&w[i]);
index = 0;
dfs(0,0,0);
built(1,n,1);
int op;
for(int i = 0 ; i < m ; i ++)
{
scanf("%d",&op);
if(op == 0)
{
int x,y;
scanf("%d%d",&x,&y);
ll data = y - w[x];
w[x] = y;
update(1,L[x],R[x],data);
}
else
{
int x;
scanf("%d",&x);
printf("%lld\n",query(1,L[x],R[x]));
}
}
}
return 0;
}