码迷,mamicode.com
首页 > 其他好文 > 详细

树链剖分学习

时间:2015-06-03 09:50:09      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:树链剖分

解决的问题
对于给出的树上两点求之间的最值或者更新操作变为logn。

其他方法
Tarjan求LCA的复杂度为 O(N+Q)所以不断更新复杂度太高。


本质: 就是将树划分为不重合的多条链每条链都有一个线段树中的编号(可类比dfs序转换线段树的想法)+线段树。在求的过程中根据重链不断逼近再用线段树维护即可。
入门文章
练习题目

一般有对点建树和对边建树两种方案,看题目要求。对第几条边进行操作这种情况要将边存储下来。
其实就是一个点的影响范围。线段树的话更新要更新到单点
模板

#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define  inf  0x3f3f3f3f
#define  maxn (100+100000)
using namespace std;
typedef long long ll;

struct Edge{
    int u,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与其父亲结点的连边在线段树中的位置   PS:这个应该是对边进行剖分
int fp[maxn];///和p相反
int son[maxn];///重儿子
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++;
}
void dfs1(int u,int pre,int d){ ///第一遍求出fa,deep,num,son
    deep[u] = d;
    fa[u] = pre;
    num[u] = 1;
    for(int i = head[u];~i;i = edge[i].next){
        int v = edge[i].to;
        if(v==pre) continue;
        dfs1(v,u,d+1);
        num[u] += num[v];
        if(son[u]==-1 || num[v] > num[son[u]])
            son[u] = v;
    }
}
///这里没有传入pre这个参数而是用fa ,是因为fa在后面有用,这样也无妨
void dfs2(int u,int sp){ /// 第二遍求出top,p,fp
    top[u] = sp;
    p[u] = pos++;
    fp[p[u]] = u;
    if(son[u]==-1)
        return;
    dfs2(son[u],sp);
    for(int i = head[u];~i;i=edge[i].next){
        int v = edge[i].to;
        if(v != son[u] && v!= fa[u])
            dfs2(v,v);
    }
}
struct Node{
    int l,r;
    ll Max;
}segTree[maxn*3];
void push_up(int i) {
    segTree[i].Max = segTree[i<<1].Max + segTree[(i<<1)|1].Max;
    ///segTree[i].Max = min(segTree[i<<1].Max , segTree[(i<<1)|1].Max);
}
 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 update(int i,int k,int val) { /// 更新线段树的第k个值为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);
}
 ll query(int i,int l,int r) { ///查询线段树中[l,r] 的最大值

     if(segTree[i].l >= l && segTree[i].r <= r) /// 这个写法和平常不一样是因为查询区间必然是重链上的点
         return segTree[i].Max;
     int mid = (segTree[i].l + segTree[i].r)/2;
     ll ans = 0;
     if(r <= mid) ans = query(i<<1,l,r);
     else if(l > mid)  ans = query((i<<1)|1,l,r);
     else {
        ans = query(i<<1,l,mid);
        ans += query((i<<1)|1,mid+1,r);
     }
     return ans;
}

ll find(int u,int v){ /// 查询u->v边的最大值
    int f1 = top[u],f2 = top[v];
    ll tmp = 0;
    while(f1 != f2){
        if(deep[f1] < deep[f2])
            swap(f1,f2),swap(u,v);
        tmp = 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 tmp + query(1,p[son[u]],p[v]); ///这个只到u所以用它孩子的连线
}

int e[maxn][3];
int main(){
    int T,cas=1;
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i = 1;i<n;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);
        dfs2(1,1);
        build(1,0,pos-1);
        for(int i = 1;i<n;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]); ///刚开始第三个参数写成这个p[e[i][2]]错了,这个这样写是因为存放边值
        }
        for(int i = 0;i<m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(a) printf("%I64d\n",find(b,c));
            else update(1,p[e[b][1]],c);///更新边还是点 点的话就是p[u] 第几条边就是 e[u][1]
        }//这句写错了wa好久
    }
    return 0;
}

树链剖分学习

标签:树链剖分

原文地址:http://blog.csdn.net/gg_gogoing/article/details/46340269

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!