题目链接:[点击进入](http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=13013)
树链剖分并不是一个复杂的算法或者数据结构,只是能把一棵树拆成链来处理而已,换一种说法,树链剖分只是xxx数据结构/算法在树上的推广,或者说,树链剖分只是把树hash到了几段连续的区间上。比如说下面这道题,就是将树分为重链和轻链然后映射到线段树上,然后再在线段树上进行查询和修改等操作。所以树链剖分的重点有两个,一是正确的将树分解成几段并映射到线段树上去,二是在线段树中进行查询和修改操作时要注意借助分解后树的性质。
下面这份代码是针对查询修改树上的边的,可以作为模板使用。
一篇讲的非常好的博客:树链剖分
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
///基于边权,修改单条边
///查询路径边权最大值
const int maxn=10010;
struct Edge
{
int to,next;
}edge[maxn*2];
int head[maxn],tot;
int top[maxn];///top[v]表示v所在链的顶端结点
int fa[maxn]; ///父节点
int deep[maxn]; ///深度
int num[maxn]; ///num[v]表示以v为根的子树的结点树
int p[maxn];///p[v]表示v与其父节点的连边在线段树中的位置
int fp[maxn]; ///表示线段树中的某个位置对应的边的起始编号
int son[maxn]; ///son[u]表示u的重儿子
int pos;
void init()
{
tot=0;
memset(head,-1,sizeof(head));
pos=0;
memset(son,-1,sizeof(son));
}
///模拟邻接表
void addedge(int u,int v)
{
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
///第一遍dfs求出fa,num,deep,son等值
void dfs1(int u,int pre,int d)
{
deep[u]=d;
fa[u]=pre;
num[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v!=pre)
{
dfs1(v,u,d+1);
num[u]+=num[v];
///确定u的重儿子
if(son[u]==-1||num[v]>num[son[u]])
son[u]=v;
}
}
}
///第二遍dfs求出top和p
void getpos(int u,int sp)
{
top[u]=sp;
p[u]=pos++;
fp[p[u]]=u;
if(son[u]==-1) return;
///保证重链上重边在线段树中的连续分布
getpos(son[u],sp);
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v!=son[u]&&v!=fa[u]) ///对其它不是重边的结点的处理
getpos(v,v);
}
}
///线段树
struct node
{
int l,r;
int Max;
}segTree[maxn*3];
void build(int i,int l,int r)
{
segTree[i].l=l;
segTree[i].r=r;
segTree[i].Max=0;
if(l==r) return;
int mid=(l+r)/2;
build(i<<1,l,mid);
build((i<<1)|1,mid+1,r);
}
void push_up(int i)
{
segTree[i].Max=max(segTree[i<<1].Max,segTree[(i<<1)|1].Max);
}
///单点更新k值为val
void update(int i,int k,int val)
{
if(segTree[i].l==k&&segTree[i].r==k)
{
segTree[i].Max=val;
return;
}
int mid=(segTree[i].l+segTree[i].r)/2;
if(k<=mid)
update(i<<1,k,val);
else
update((i<<1)|1,k,val);
push_up(i);
}
///区间查询:[l,r]中的最大值
int query(int i,int l,int r)
{
if(segTree[i].l==l&&segTree[i].r==r)
return segTree[i].Max;
int mid=(segTree[i].l+segTree[i].r)/2;
if(r<=mid) return query(i<<1,l,r);
else if(l>mid) return query((i<<1)|1,l,r);
else return max(query(i<<1,l,mid),query((i<<1)|1,mid+1,r));
}
///查找u->v边的最大值
int find(int u,int v)
{
int f1=top[u];
int f2=top[v];
int tmp=0;
while(f1!=f2)
{
///总是保证deep[f1]>=deep[f2]
if(deep[f1]<deep[f2])
{
swap(f1,f2);
swap(u,v);
}
tmp=max(tmp,query(1,p[f1],p[u]));
u=fa[f1]; f1=top[u];
}
if(u==v) return tmp;
if(deep[u]>deep[v])
swap(u,v);
return max(tmp,query(1,p[son[u]],p[v]));
}
int e[maxn][3];
int main()
{
int T;
int n,u,v;
// freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
for(int i=0;i<n-1;i++)
{
scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
///注意添加的是双向边
addedge(e[i][0],e[i][1]);
addedge(e[i][1],e[i][0]);
}
dfs1(1,0,0);
getpos(1,1);
build(1,0,pos-1);
///下面是利用每条边更新线段树的操作
for(int i=0;i<n-1;i++)
{
if(deep[e[i][0]]>deep[e[i][1]])
swap(e[i][0],e[i][1]);
update(1,p[e[i][1]],e[i][2]);
}
char op[10];
while(scanf("%s",op))
{
if(op[0]==‘D‘) break;
scanf("%d%d",&u,&v);
if(op[0]==‘Q‘)
printf("%d\n",find(u,v)); ///查询
else
update(1,p[e[u-1][1]],v); ///修改
}
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-15 02:34:39