樹鏈剖分+線段樹

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