树链剖分+线段树

之前就学过树链剖分的原理,只会树链剖分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));
        }
    }
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章