这篇blog算是对链剖的笔记吧
先放下题:
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和。注意:从点u到点v的路径上的节点包括u和v本身,1<=n<=30000,0<=q<=200000,每个节点的权值w在-30000到30000之间。
写写我对树链剖分的一点体会:
线段树维护信息:可利用懒标记
放代码
#include<stdio.h> #include<stdlib.h> #define INF 100000000 int u[60005]={0},v[60005]={0},first[30005]={0},next[60005]={0}; int fa[30005]={0},deep[30005]={0},size[30005]={0},son[30005]={0},top[30005]={0},pos[30005]={0}; //fa:父节点,deep:深度(0开始),size:后代数(含自身),son:重孩子,top:所在树链头,pos:此点在线段树中序号 int sumv[150000]={0},maxv[150000]={0}; int n,e=0,tot=0; int max(int a,int b) { if(a>b) return a; return b; } void jh(int* a,int* b) { int t=*a; *a=*b; *b=t; } void tj(int x,int y) { u[++e]=x; v[e]=y; next[e]=first[x]; first[x]=e; } void dfs1(int x,int pre,int dep)//求:fa,deep,size,son { int i; fa[x]=pre; deep[x]=dep; size[x]=1; for(i=first[x];i!=0;i=next[i]) if(v[i]!=pre) { dfs1(v[i],x,dep+1); size[x]+=size[v[i]]; if(son[x]==0||size[son[x]]<size[v[i]]) son[x]=v[i]; } } void dfs2(int x,int t)////求:top,pos,t:x所属的重链链头 { int i; top[x]=t; pos[x]=++tot; if(son[x]!=0) dfs2(son[x],t); for(i=first[x];i!=0;i=next[i]) if(v[i]!=fa[x]&&v[i]!=son[x]) dfs2(v[i],v[i]); } void xg(int p,int x,int o,int left,int right)//单点修改 { int mid=(left+right)/2; if(left==right) { sumv[o]=maxv[o]=p; return; } if(x<=mid) xg(p,x,o*2,left,mid); if(x>mid) xg(p,x,o*2+1,mid+1,right); sumv[o]=sumv[o*2]+sumv[o*2+1]; maxv[o]=max(maxv[o*2],maxv[o*2+1]); } int cx_max(int x,int y,int o,int left,int right) { int mid=(left+right)/2,ans=-INF; if(x<=left&&right<=y) return maxv[o]; if(x<=mid) ans=max(ans,cx_max(x,y,o*2,left,mid)); if(y>mid) ans=max(ans,cx_max(x,y,o*2+1,mid+1,right)); return ans; } int cx_sum(int x,int y,int o,int left,int right) { int mid=(left+right)/2,ans=0; if(x<=left&&right<=y) return sumv[o]; if(x<=mid) ans+=cx_sum(x,y,o*2,left,mid); if(y>mid) ans+=cx_sum(x,y,o*2+1,mid+1,right); return ans; } int getmax(int x,int y) { int tx=top[x],ty=top[y],ans=-INF; while(tx!=ty)//x,y不在同一重链上 { if(deep[tx]<deep[ty]) { jh(&x,&y); jh(&tx,&ty); } ans=max(ans,cx_max(pos[tx],pos[x],1,1,n)); x=fa[tx]; tx=top[x]; } if(deep[x]>deep[y]) jh(&x,&y); return max(ans,cx_max(pos[x],pos[y],1,1,n));//因为同一重链上,deep小的pos小 } int getsum(int x,int y) { int tx=top[x],ty=top[y],ans=0; while(tx!=ty) { if(deep[tx]<deep[ty]) { jh(&x,&y); jh(&tx,&ty); } ans+=cx_sum(pos[tx],pos[x],1,1,n); x=fa[tx]; tx=top[x]; } if(deep[x]>deep[y]) jh(&x,&y); return ans+cx_sum(pos[x],pos[y],1,1,n); } int main() { char s[20]={0}; int Q,i,x,y; scanf("%d",&n); for(i=1;i<n;i++) { scanf("%d%d",&x,&y); tj(x,y); tj(y,x); } dfs1(1,0,0); dfs2(1,1); for(i=1;i<=n;i++) { scanf("%d",&x); xg(x,pos[i],1,1,n); } scanf("%d\n",&Q); for(;Q>0;Q--) { scanf("%s%d%d",s,&x,&y); if(s[1]=='H') xg(y,pos[x],1,1,n);//CHANGE u t : 把结点u的权值改为t if(s[1]=='M') printf("%d\n",getmax(x,y));//QMAX u v: 询问从点u到点v的路径上的节点的最大权值 if(s[1]=='S') printf("%d\n",getsum(x,y));//QSUM u v: 询问从点u到点v的路径上的节点的权值和 } return 0; }