题意 给n个点的树,有两种操作 op x y ,第一种操作,将第x条边修改权值为y,第二种操作查询x到y的路径长度,第三种操作就是查询x到y的路径中边权的最大值。
今天刚学,说下我目前的理解,树链剖分就是将树上的边按照一种序排列到线段树上,从而进行快速的查询和更新。
对树的拆分,分为轻链和重链,快速的查询多是在重链上,轻链一般还是要一个个走,所以查询的时候,多是将轻链上的点跳到重链上,从而对多个重链来处理达到查询的目的。
【ps 练习打的,代码不一定对】
代码
#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
#define ll o<<1
#define rr o<<1|1
#define lson o<<1,le,mid
#define rson o<<1|1,mid+1,ri
const int MAXN = 1e3 ;
const int MAXM = 1e5 ;
const int mod = 1e9+7 ;
const int inf = 0x3f3f3f3f ;
struct Tree{
int l,r;
LL sum,Max;
}tree[MAXN<<2];
void Up(int o) {
tree[o].sum=tree[ll].sum+tree[rr].sum;
tree[o].Max=max(tree[ll].Max,tree[rr].Max);
}
void Build(int o,int le,int ri){
tree[o].l=le;tree[o].r=ri;
tree[o].sum=0;tree[o].Max=0;
if(le==ri) return ;
int mid=(le+ri)>>1;
Build(lson);Build(rson);
Up(o);
}
void UpDate(int o,int pos,int val){ // 边权更新
if(tree[o].l==tree[o].r) {
tree[o].sum=val;
tree[o].Max=val;
return ;
}
int mid=(tree[o].l+tree[o].r)>>1;
if(pos>mid) UpDate(rr,pos,val);
else if(pos<=mid) UpDate(ll,pos,val);
Up(o);
}
LL QuerySum(int o,int le,int ri){
if(tree[o].l>=le&&tree[o].r<=ri) return tree[o].sum;
int mid=(tree[o].l+tree[o].r)>>1;
if(le>mid) return QuerySum(rr,le,ri);
else if(ri<=mid) return QuerySum(ll,le,ri);
else return QuerySum(ll,le,mid)+QuerySum(rr,mid+1,ri);
}
LL QueryMax(int o,int le,int ri){
if(tree[o].l>=le&&tree[o].r<=ri) return tree[o].Max;
int mid=(tree[o].l+tree[o].r)>>1;
if(le>mid) return QueryMax(rr,le,ri);
else if(ri<=mid) return QueryMax(ll,le,ri);
else return max(QueryMax(ll,le,mid),QueryMax(rr,mid+1,ri));
}
// 以上为线段树模板部分
struct Edge {
int from,to,next;
}edge[MAXN<<1];
int head[MAXN],topp;
void init(){
memset(head,-1,sizeof(head));
topp=0;
}
void addedge(int a,int b){
Edge e={a,b,head[a]};
edge[topp]=e;head[a]=topp++;
}
int son[MAXN],num[MAXN];
int top[MAXN],pos[MAXN],id;
int dep[MAXN],pre[MAXN];
void Dfs1(int u,int fa,int d){
dep[u]=d;pre[u]=fa;son[u]=-1;
for(int i=head[u];i!=-1;i=edge[i].next){
Edge e=edge[i];
if(e.to==fa) continue;// 自环跳过
Dfs1(e.to,u,d+1);
num[u]+=num[e.to];
if(son[u]==-1||num[son[u]]<num[e.to]) son[u]=e.to;
}
}
void Dfs2(int u,int t){
top[u]=t; pos[u]=++id;
if(son[u]==-1) return ;// 子叶节点
Dfs2(son[u],t); // 走重链
for(int i=head[u];i!=-1;i=edge[i].next){
Edge e=edge[i];
if(e.to==pre[u]||e.to==son[u]) continue;//如果自环或者是重链 跳过。
Dfs2(e.to,e.to); // 走轻链,这里可以知道,不是重链上的点,top[v]=v;
}
}
LL GetSum(int u,int v){ /* 路径求和 */
int f1=top[u],f2=top[v];
LL ans=0;
while(f1!=f2) {// 不在一条链上。
if(dep[f1]<dep[f2]) {
swap(u,v); swap(f1,f2);
}
ans+=QuerySum(1,pos[f1],pos[u]); // 每次让深的节点上跳。 轻链就跳一个节点,重链就跳到头节点。
u=pre[f1],f1=top[u];
}
if(u==v) return ans;
if(dep[u]>dep[v]) swap(u,v);
return ans+=QuerySum(1,pos[son[u]],pos[v]);
}
LL GetMax(int u,int v){ /* 路径上边权最大 */
int f1=top[u];int f2=top[v];
LL ans=0;
while(f1!=f2){
if(dep[f1]<dep[f2]) {
swap(f1,f2);swap(u,v);
}
ans=max(ans,QueryMax(1,pos[f1],pos[u]));
u=pre[f1];f1=top[u];
}
if(u==v) return ans;
if(dep[u]>dep[v]) swap(u,v);
return max(ans,QueryMax(1,pos[son[u]],pos[v]));
}
int s[MAXN],e[MAXN],c[MAXN];
int main(){
int n,q;
while(scanf("%d%d",&n,&q)!=EOF){
init();
for(int i=1;i<=n-1;i++) {
scanf("%d %d %d",&s[i],&e[i],&c[i]);
addedge(s[i],e[i]);
addedge(e[i],s[i]);
}
Dfs1(1,-1,1);
id=0; Dfs2(1,1);
Build(1,1,id);
for(int i=1;i<=n-1;i++){// 一个节点和其父边相对应,序号为线段树中的位置。pos[v]
if(dep[s[i]]>dep[e[i]]) swap(s[i],e[i]); // 所以这里要找到最深的节点。
UpDate(1,pos[e[i]],c[i]); //线段树赋初值。
}
while(q--){
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(op==0) UpDate(1,pos[e[x]],y);
else if(op==1) printf("%lld\n",GetSum(x,y));
else if(op==2) printf("%lld\n",GetMax(x,y));
}
}
return 0;
}