树链剖分模板+入门题 SPOJ - QTREE

来源:互联网 发布:大数据的理解 编辑:程序博客网 时间:2024/05/16 09:23

题目链接:[点击进入](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和pvoid 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值为valvoid 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;}
1 1
原创粉丝点击