1. 程式人生 > >樹鏈剖分-入門題目

樹鏈剖分-入門題目

                                                                              Query on a tree

題目大致意思就是: 給你一棵樹,有連個操作:

      ● 第一個是查詢任意兩個不同節點上的最短路徑上的最大權邊!

      ● 第二個操作修改某一條邊的權值;

對於一棵樹,數的深度如果很大,那麼每次查詢兩個葉子節點,時間複雜度還是很高的。所以我們就把樹分成一條一條的樹鏈,所謂樹鏈就是:沿著樹的一條路徑。 得到路徑後,我們可以用堆,線段樹進行維護。在一些題目中,樹鏈剖分題目,一般可以用dfs 進行剖鏈。

     鄙人學習樹鏈剖分所用到資料:

              ,看這篇部落格主要知道什麼是樹鏈剖分,以及原理;知道樹鏈剖分部分,剖分樹鏈之後需要用一些資料結構維護,大部分用線段樹。線段樹比較常用。所以我也用線段樹;

有了以上知識儲備,做這個題目也比較輕鬆! 線段樹要會!!可以參考kuangbin題解來做;附上我AC程式碼,一是為了存個模板,二還請大佬指出錯誤!

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<deque>
#include<stack>
#include<string>
#include<vector>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<time.h>

using namespace std;
typedef long long LL;
const int INF=2e9+1e8;
const int MOD=1e9+7;
const int MAXSIZE=2e4+100;
const double eps=0.0000000001;
void fre()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
}
#define memst(a,b) memset(a,b,sizeof(a))
#define fr(i,a,n) for(int i=a;i<n;i++)

int son[MAXSIZE],deep[MAXSIZE],father[MAXSIZE],num[MAXSIZE],top[MAXSIZE],p[MAXSIZE],fp[MAXSIZE],pos;
int Treevalue[MAXSIZE];
// son 代表重兒子; deep 深度;father 父親節點;num 統計節點個數;
//top 樹鏈的頭, p 某個節點線上段樹的位置 ; fp 線段樹某個位置在樹中的位置;
//pos 是用來對樹的節點進行標號的;

// 建圖部分
int first[MAXSIZE],ntot;
struct  Node
{
    int to,next,val;
};
struct NODE
{
    int a,b,c;
} input[MAXSIZE];
Node edge[MAXSIZE];
void init()
{
    memst(first,-1);
    memst(son,-1);
    pos=ntot=0;
}
void addedge(int s,int t,int val)
{
    edge[ntot].to=t,edge[ntot].val=val;
    edge[ntot].next=first[s],first[s]=ntot++;
}
// 剖分樹鏈
void dfs(int x,int pre,int _deep)
{
    deep[x]=_deep;
    father[x]=pre;
    num[x]=1;
    for(int i=first[x]; i!=-1; i=edge[i].next)
    {
        int to=edge[i].to;
        if(to!=pre)
        {
            Treevalue[to]=edge[i].val;
            dfs(to,x,_deep+1);
            num[x]+=num[to];
            if(son[x]==-1||num[to]>num[son[x]]) son[x]=to;
        }
    }
    return ;
}
void getlist(int x,int pr)
{
    top[x]=pr;
    if(son[x]!=-1) // 如果存在重兒子,就沿著一直找下去
    {
        p[x]=pos++;
        fp[p[x]]=x;
        getlist(son[x],pr);
    }
    else //直到到低端為止
    {
        p[x]=pos++;
        fp[p[x]]=x;
        return ;
    }
    for(int i=first[x]; i!=-1; i=edge[i].next)
    {
        int to=edge[i].to;
        if(to!=son[x]&&to!=father[x]) getlist(to,to); //如果不是往回走 且 不是重兒子那麼就是新的樹鏈進行dfs金合歡花;
    }
}
//樹鏈剖分部分 完成

// 線段樹部分
struct TreeNode
{
    int l,r,max;
} tree[MAXSIZE*3];
void push_up(int i)
{
    tree[i].max=max(tree[i<<1].max,tree[i<<1|1].max);
}
void build(int i,int l,int r)
{
    tree[i].l=l,tree[i].r=r,tree[i].max=0;
    if(l==r)
    {
        tree[i].max=Treevalue[fp[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    push_up(i);
}
int query(int i,int l,int r)
{
    if(tree[i].l==l&&tree[i].r==r) return tree[i].max;
    int mid=(tree[i].l+tree[i].r)>>1;
    if(l>mid) return query(i<<1|1,l,r);
    else if(r<=mid) return query(i<<1,l,r);
    else return max(query(i<<1|1,mid+1,r),query(i<<1,l,mid));
}
int getmax(int a,int b)
{
    int res=0;
    int f1=top[a],f2=top[b];
    while(f1!=f2)
    {
        if(deep[f1]>deep[f2])
        {
            res=max(res,query(1,p[f1],p[a]));
            a=father[f1],f1=top[a];
        }
        else
        {
            res=max(res,query(1,p[f2],p[b]));
            b=father[f2],f2=top[b];
        }
    }
    if(a==b) return res;
    if(deep[a]>deep[b]) swap(a,b);
    return max(res,query(1,p[son[a]],p[b]));
}

void update(int i,int k,int val)//插點
{
    if(tree[i].l==k&&tree[i].r==k)
    {
        tree[i].max=val;
        return ;
    }
    int mid=(tree[i].l+tree[i].r)>>1;
    if(k>mid) update(i<<1|1,k,val);
    else update(i<<1,k,val);
    push_up(i);
}
void Debug(int i,int l,int r)
{
    if(tree[i].r>r||tree[i].l<l) return ;
    printf("i=%d l=%d r=%d max=%d\n",i,tree[i].l,tree[i].r,tree[i].max);
    Debug(i<<1,l,r);
    Debug(i<<1|1,l,r);
}
int main()
{
    int ncase,n;
    scanf("%d",&ncase);
    while(ncase--)
    {
        init();
        scanf("%d",&n);
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&input[i].a,&input[i].b,&input[i].c);
            addedge(input[i].a,input[i].b,input[i].c);
            addedge(input[i].b,input[i].a,input[i].c);
        }
        dfs(1,0,0);//把圖變成一棵樹;
        getlist(1,1);//剖樹鏈;
        build(1,1,n-1);
        while(1)
        {
            char opt[10];

            scanf("%s",opt);
            if(opt[0]=='D') break;
            if(opt[0]=='C')
            {
                int change_pos,value;
                scanf("%d%d",&change_pos,&value);
                int a=input[change_pos].a,b=input[change_pos].b;
                if(deep[a]>deep[b]) swap(a,b);
                update(1,p[b],value);
            }
            else
            {
                int a,b;
                scanf("%d%d",&a,&b);
                printf("%d\n",getmax(a,b));
            }
        }
    }
    return 0;
}
/**************************************************/
/**             Copyright Notice                 **/
/**  writer: wurong                              **/
/**  school: nyist                               **/
/**  blog  : http://blog.csdn.net/wr_technology  **/
/**************************************************/