解决问题:给出一个树,有两个操作:1.修改某条树边的权值。 2.求某个节点到另一个的路径中的最大边权值。
基本原理:如果不是一棵树,而是一条链,那么很清楚用线段树做。而现在就是一棵树,那么我们就可以将树分成一条一条的链。
算法步骤:
1.构造:我们称这些链为重路径,每两条重路径之间由一条轻边连接,对于一棵树,每一个父节点u有且仅有一条重路连向一个子节点v,这个子节点v必须满足size[v]>=size[v`](v`指u的其他子节点)。通过dfs,我们就可以剖分整棵树了。
2.修改:找到点所在的边所在的重路径,用线段树的点修改可以很好的完成。
3.查找:分析两个点a和b,因为我们重路径的最大值可以用线段树的最大值查找,十分方便的求出来,我们就可以类比LCA的思路,比较a,b所在的重路径的顶端的深度,较深的点向上爬至顶部,循环,最后a和b要么是同一个点,算法结束,要么在同一个重路径中,最后调用一次线段树的最大值查找。
算法复杂度:O(T+q*(log n)^2)
参考程序:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define infi 0x7fffffff
#define maxn 10100
using namespace std;
struct edge{
int loc,code,next;//loc边对应点的编号,code边的编号
};
struct TreeNode{
int l,r,max;
int lch,rch;
};
int tree[maxn],nodeCnt;
TreeNode node[maxn*10];
edge e[maxn*2];
int edgeCnt,a[maxn];
int father[maxn],//每个点的父节点
path_top[maxn],//路径的顶部结点
path_size[maxn],//路径的长度
path_dep[maxn],//路径顶部结点的深度
size[maxn],//每个节点的子树节点数
bottom[maxn],//每个点对应的边
belong[maxn],//每条边的路径编号
rank[maxn],//结点在路径中的深度(最深的为1)
w[maxn],
path_count,n;
void addedge(int x,int y,int z,int num){
int p;
p=++edgeCnt;e[p].loc=y;e[p].code=num;
w[num]=z;e[p].next=a[x];a[x]=p;
p=++edgeCnt;e[p].loc=x;e[p].code=num;
w[num]=z;e[p].next=a[y];a[y]=p;
}//构造树边
void dfs(int k,int dep){
dep++;
int p=a[k];size[k]=1;
int max=0,j;
while (p){
if (e[p].loc !=father[k]){//边所连的结点不是父节点
father[e[p].loc]=k;bottom[e[p].code]=e[p].loc;
dfs(e[p].loc,dep);
size[k]+=size[e[p].loc];
if (size[e[p].loc]>max){
max=size[e[p].loc];
j=e[p].loc;
}
}
p=e[p].next;
}
p=a[k];belong[k]=0;
while (p){
if (e[p].loc != father[k]){
if (e[p].loc==j){
belong[k]=belong[e[p].loc];
rank[k]=rank[e[p].loc]+1;
}
else{
int i=belong[e[p].loc];
path_dep[i]=dep;
path_size[i]=rank[e[p].loc];
path_top[i]=e[p].loc;
}
}
p=e[p].next;
}
if (belong[k]==0){
belong[k]=++path_count;
rank[k]=1;
}//叶节点自为一条路径
}
void build(int p,int l,int r){
node[p].l=l;
node[p].r=r;
node[p].max=-infi;
if (r-1>l){
node[p].lch=++nodeCnt;
build(node[p].lch,l,(l+r)/2);
node[p].rch=++nodeCnt;
build(node[p].rch,(l+r)/2,r);
}
else node[p].lch=node[p].rch=0;
}
void change(int p,int l,int data){
if (l<=node[p].l && node[p].r<=l+1)
node[p].max=data;
else{
if (l<(node[p].l+node[p].r)/2)change(node[p].lch,l,data);
else change(node[p].rch,l,data);
node[p].max=max(node[node[p].lch].max,node[node[p].rch].max);
}
}
int ask(int p,int l,int r){
int t1,t2;
if (l<=node[p].l && node[p].r<=r)return (node[p].max);
else {
t1=t2=-infi;
if (l<(node[p].l+node[p].r)/2)t1=ask(node[p].lch,l,r);
if (r>(node[p].l+node[p].r)/2)t2=ask(node[p].rch,l,r);
return max(t1,t2);
}
}//线段树的基本操作
void prepare(){
path_count=father[1]=0;
dfs(1,0);
int i=belong[1];
path_dep[i]=0;
path_size[i]=rank[1];
path_top[i]=1;
for (int i=1;i<=path_count;i++){
tree[i]=++nodeCnt;
build(tree[i],1,path_size[i]+1);
}//为每一条路径构造线段树
for (int i=1;i<n;i++)
change(tree[belong[bottom[i]]],rank[bottom[i]],w[i]);//赋权值
}//树链剖分的主过程
void work(int x,int y){
change(tree[belong[bottom[x]]],rank[bottom[x]],y);
}
int query(int a,int b){
int max=-infi,x=belong[a],y=belong[b];
int k;
while (x!=y){
if (path_dep[x]>path_dep[y]){
k=ask(tree[x],rank[a],path_size[x]+1);
if (k>max)max=k;
a=father[path_top[x]];
x=belong[a];
}
else{
k=ask(tree[y],rank[b],path_size[y]+1);
if (k>max)max=k;
b=father[path_top[y]];
y=belong[b];
}
}
if (rank[a]!=rank[b]){
if (rank[a]>rank[b])k=ask(tree[belong[a]],rank[b],rank[a]);
else k=ask(tree[belong[a]],rank[a],rank[b]);
if (k>max)max=k;
}
return max;
}
int main(){
int T;
char cmd;
scanf("%d",&T);
while (T--){
scanf("%d",&n);
for (int i=1;i<n;i++){
int x,y,z;
scanf("%d %d %d\n",&x,&y,&z);
addedge(x,y,z,i);
}
prepare();
while (true){
cmd=getchar();
if (cmd=='D')break;
int x,y;
scanf("%d %d\n",&x,&y);
if (cmd=='Q')printf("%d\n",query(x,y));
else work(x,y);
}
}
return 0;
}