我是在学了splay之后做这题的。 对于树链的操作,感觉旋转一下也没什么问题。
可以归纳下需要解决的问题。
一是如何将多叉树,类似的转变成二叉来做。
二是如何高效的确定同一棵树上的两个节点的lca(也就是最近公共祖先)
我学的时候,是看这里的题解的: http://www.cnblogs.com/kuangbin/archive/2013/09/04/3300251.html
动态树的资料可以参考:
http://wenku.baidu.com/view/1845fdc75fbfc77da269b1ae.html
http://wenku.baidu.com/view/e7c220c52cc58bd63186bdae.html
什么叫access操作? 如果不懂这个,你就无法理解它的巧妙了。
它是将从该节点一直到根节点的这条路径标记出来,修改它的一个标志属性,来表示该节点是已经被选择的。
题解中的实现方式是
1、如果该点是选择的点,则标记成rt[u]=false
2、如果该点不是选择的点,但却遍历到了,就标记成rt[u]=true;
这样,我们讨论如何如下
1、 如何使一个点变成根
因为你已经把这条路径标记成了 false
则可以使用Splay操作,直接一直旋转到根。
2、如何获得lca(最近公共祖先)
通过u这个节点,标记出一条false 链后,
再通过v这个节点,不断向上标记,如果发现上一层是false的节点,那么它所调用的Splay会直接将该点旋转至于0之下,自动return掉了。 此为相当巧妙的地方。
需要注意的一点是,mroot操作之后需要翻转下左右链。 因为它是从ch[u][1],这条链翻转上去的。。
额。大致来说就是这样。下次得做些别的题目来巩固了。
//
// hdu4010.cpp
// ACM_HDU
//
// Created by ipqhjjybj on 13-9-11.
// Copyright (c) 2013年 ipqhjjybj. All rights reserved.
//
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAXN 333333
using namespace std;
int n,head[MAXN],cnt;
int pre[MAXN],ch[MAXN][2],add[MAXN],rev[MAXN],Max[MAXN],key[MAXN];
bool rt[MAXN];
int max(int a,int b){
if(a>b)return a;
else return b;
}
struct node{
int to,next;
}Edge[MAXN<<1];
void addEdge(int u,int v){
Edge[cnt].to=v;
Edge[cnt].next=head[u];
head[u]=cnt++;
}
void dfs(int u){
for(int q=head[u];q!=-1;q=Edge[q].next){
int v=Edge[q].to;
if(pre[v]!=0)continue;
pre[v]=u;
dfs(v);
}
}
void Update_add(int u,int w){
if(!u)return;
key[u]+=w;
add[u]+=w;
Max[u]+=w;
}
void Update_rev(int u){
if(!u)return;
swap(ch[u][1],ch[u][0]);
rev[u]^=1;
}
void PushUp(int u){
Max[u]=max(max(Max[ch[u][0]],Max[ch[u][1]]),key[u]);
}
void PushDown(int u){
if(add[u]){
Update_add(ch[u][0], add[u]);
Update_add(ch[u][1],add[u]);
add[u]=0;
}
if(rev[u]){
Update_rev(ch[u][0]);
Update_rev(ch[u][1]);
rev[u]=0;
}
}
void Rotate(int x){
int y=pre[x];int kind=(ch[y][1]==x);
ch[y][kind]=ch[x][!kind];
pre[ch[y][kind]]=y;
pre[x]=pre[y];
pre[y]=x;
ch[x][!kind]=y;
if(rt[y])
rt[y]=false,rt[x]=true;
else ch[pre[x]][ch[pre[x]][1]==y]=x;
PushUp(y);
}
void P(int u){
if(!rt[u]) P(pre[u]);
PushDown(u);
}
void Splay(int u){
P(u);
while(!rt[u]){
int f=pre[u],ff=pre[f];
if(rt[f])
Rotate(u);
else{
if((ch[ff][1]==f)==(ch[f][1]==u))
Rotate(f),Rotate(u);
else Rotate(u),Rotate(u);
}
}
PushUp(u);
}
int Access(int u){
int f;
for(f=0;u;u=pre[f=u]){
Splay(u);
rt[ch[u][1]]=true,rt[ch[u][1]=f]=false;
PushUp(u);
}
return f;
}
bool judge(int u,int v){
while(pre[u])u=pre[u];
while(pre[v])v=pre[v];
return u==v;
}
void mroot(int u){
Access(u);
Splay(u);
Update_rev(u);
}
void lca(int &u,int &v){
Access(v);
for(v=0;u;u=pre[v=u]){
Splay(u);
if(pre[u]==0)return;
rt[ch[u][1]]=true,rt[ch[u][1]=v]=false;
PushUp(u);
}
}
void Link(int u,int v){
if(judge(u,v)){
puts("-1");
return;
}
mroot(u);
pre[u]=v;
}
void Add(int u,int v,int w){
if(!judge(u,v)){
puts("-1");
return;
}
lca(u,v);
Update_add(ch[u][1], w);
Update_add(v,w);
key[u]+=w;
PushUp(u);
}
void Cut(int u,int v){
if(u==v||!judge(u,v)){
puts("-1");
return;
}
mroot(u);
Splay(v);
pre[ch[v][0]]=pre[v];
pre[v]=0;
rt[ch[v][0]]=true;
ch[v][0]=0;
PushUp(v);
}
void Query(int u,int v){
if(!judge(u, v)){
puts("-1");
return;
}
lca(u,v);
printf("%d\n",max(max(Max[ch[u][1]],Max[v]),key[u]));
}
int main(){
int q,cmd,a,b,c;
while(scanf("%d",&n)!=EOF){
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1,u,v;i<n;i++){
scanf("%d %d",&u,&v);
addEdge(u,v);
addEdge(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",key+i);
key[0]=0;
for(int i=0;i<=n;i++){
ch[i][0]=ch[i][1]=pre[i]=rev[i]=add[i]=0;
Max[i]=key[i];
rt[i]=true;
}
Max[0]=-122222;
pre[1]=-1;
dfs(1);
pre[1]=0;
scanf("%d",&q);
while(q--){
scanf("%d",&cmd);
if(cmd==1){
scanf("%d %d",&a,&b);
Link(a,b);
}else if(cmd==2){
scanf("%d %d",&a,&b);
Cut(a,b);
}else if(cmd==3){
scanf("%d %d %d",&a,&b,&c);
Add(b,c,a);
}else{
scanf("%d %d",&a,&b);
Query(a,b);
}
//printf("q=%d a=%d b=%d\n",q,a,b);
}
printf("\n");
}
return 0;
}
下次就完全自己手写了。