之前就学过树链剖分的原理,只会树链剖分LCA,今天做了一道“树链剖分+数据结构”的题,虽然很简单,但可以当模板用一用,粘到这里。
树链剖分简单描述(可能不大对):
- 第一步就是划分轻重边,按每一棵子树的大小,与形成子树最大的一个子节点是重边,其余为轻边,然后就得到了轻重链。
- 之后就可以用数据结构维护一些东西了,可以是点也可以是边。
- 对节点 x 到 y 间的路径进行操作时,分别找到 x 和 y 所在链(无论轻重)的顶,若相同,直接往上跳并维护操作,若不同,让链顶深度大的向上跳到它的父节点,找到一条新链,继续上述操作(感觉跟树链剖分LCA差不多,最终他们会跳到同一个点)。
模板题:SPOJ 375
题目大意:给出一棵树,每条边编号 1~n-1 有权值,可修改第i条边的权值,支持询问从节点 x 到节点 y 的路径上的最大边权。
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 10010
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define Mid(x,y) ((x+y)>>1)
using namespace std;
struct edge{
int from,to,ne;
}e[N<<1];
int head[N],tot=0;
void push(int x,int y)
{
e[++tot].from=x; e[tot].to=y; e[tot].ne=head[x]; head[x]=tot;
e[++tot].from=y; e[tot].to=x; e[tot].ne=head[y]; head[y]=tot;
}
int fa[N],siz[N],son[N],w[N],p[N],deep[N],tree_id;
int fp[N];
struct node{
int l,r,mx;
}tree[N<<2];
void build(int l,int r,int now) //线段树维护最大边权
{
tree[now].l=l; tree[now].r=r;
tree[now].mx=0;
if (l==r) return;
int mid=Mid(l,r);
build(l,mid,L(now));
build(mid+1,r,R(now));
}
void update(int pos,int val,int now)
{
if (tree[now].l==tree[now].r)
{
tree[now].mx=val;
return;
}
int mid=Mid(tree[now].l,tree[now].r);
if (pos<=mid) update(pos,val,L(now));
else update(pos,val,R(now));
tree[now].mx=max(tree[L(now)].mx,tree[R(now)].mx);
}
int query(int l,int r,int now)
{
if (l==tree[now].l&&r==tree[now].r)
return tree[now].mx;
int mid=Mid(tree[now].l,tree[now].r);
if (r<=mid) return query(l,r,L(now));
else if (l>mid) return query(l,r,R(now));
else return max(query(l,r,L(now)),query(l,r,R(now)));
}
int find(int x,int y) //往上跳
{
int f1=p[x],f2=p[y];
int tmp=0;
while (f1!=f2)
{
if (dep[f1]<dep[f2])
swap(f1,f2),swap(x,y);
tmp=max(tmp,query(w[f1],w[x],1));
x=fa[f1],f1=p[x];
}
if (x==y) return tmp;
if (dep[x]>dep[y]) swap(x,y);
return max(tmp,query(w[son[x]],w[y],1));
}
void dfs(int now,int pre,int dep) //轻重边
{
son[now]=0,fa[now]=pre,siz[now]=1,deep[now]=dep;
for (int i=head[now];i;i=e[i].ne)
{
int v=e[i].to;
if (v==fa) continue;
dfs(v,now,dep+1);
siz[now]+=siz[v];
if (siz[v]>siz[son[now]]) son[now]=v;
}
}
void dfs2(int now,int pre) //轻重链
{
w[now]=++tree_id; /* now节点在线段树上的位置 */
p[now]=pre; /* now所在链的链顶 */
if (son[now])
dfs2(son[now],pre);
else return;
for (int i=head[now];i;i=e[i].ne)
{
int v=e[i].to;
if (v!=fa&&v!=son[now]) dfs2(v,v);
}
}
int n,D[N][3];
void init()
{
memset(head,0,sizeof(head)); tot=0;
memeset(son,0,sizeof(son));
tree_id=0;
}
int main()
{
int T;
scanf("%d",&T);
char s[10];
while (T--)
{
scanf("%d",&n);
init();
for (int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
D[i][0]=x,D[i][1]=y,D[i][2]=z;
}
dfs(1,1,0);
dfs2(1,1);
build(1,n,1);
for (int i=1;i<n;i++)
{
if (dep[D[i][0]]>dep[D[i][1]]) swap(D[i][1],D[i][0]);
update(w[D[i][1]],D[i][2],1);
}
while (scanf("%s",s))
{
if (s[0]=='D') break;
int x,y;
scanf("%d%d",&x,&y);
if (s[0]=='C') update(w[D[x][1]],y,1);
else printf("%d\n",find(x,y));
}
}
}