树链剖分模板

/*
节点的标号从1开始
 */
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10000;  //N为节点的个数
struct e{
    int v;
    e* nxt;
}es[N<<1], *fir[N];
struct node{
    int ls, rs; //左右儿子的下标,为-1表示空
    int l, r;   //区间的左右标号
    //数据域,根据不同的需要添加,数据的down和update和线段树的无异
    int mid() { return (l + r) >> 1;  }
}nodes[N<<1];
int n, en;
int que[N], par[N], dep[N], root[N], seg[N], st[N], ed[N], top[N], sons[N], id[N];
//que用于BFS,par记录父节点,dep记录节点的深度。 root[i]为链i的根节点,seg用于在链上建线段树,
//st[i],ed[i]分别为链i的左右端点,top[i]为链i的顶部的节点,sons[i]为节点i的儿子节点
//id[i]是节点i所属的链的标号,
int ln, cnt, tr; //ln是链的个数,cnt为节点的个数,tr是树的根节点
inline void add_e(int u, int v){
    es[en].v = v;
    es[en].nxt = fir[u];
    fir[u] = &es[en++];
}
inline void newNode(int& id, int l, int r){
    nodes[cnt].ls = nodes[cnt].rs = -1;
    nodes[cnt].l = l;
    nodes[cnt].r = r;
    id = cnt++;
}
void build(int& id, int l, int r){ //在剖分出来的链上构建线段树
    newNode(id, l, r);
    if(l >= r){
    	//seg[l]为落在这个线段树节点上的原树中的节点
        return ;
    }
    int mid = (l+r)>>1;
    build(nodes[id].ls, l, mid);
    build(nodes[id].rs, mid+1, r);
}
void initTree(){  //初始化剖分树
    //确定父亲
    int l, r, u, v, i;
    e* cur;
    l = r = 0;
    que[r++] = tr;
    par[tr] = -1;
    dep[tr] = 0;
    while(l != r){
        u = que[l++];
        int g = 1;
        for(cur = fir[u]; cur; cur = cur->nxt){
            if((v = cur->v) != par[u]){
                que[r++] = v;
                par[v] = u;
                dep[v] = dep[u]+1;
            }
        }
    }
    //计算子树大小
    for(i = 1; i <= n; i++){
        sons[i] = 1;
        id[i] = -1;
    }
    for(i = r-1; i >= 0; i--){
        u = que[i];
        if(par[u] >= 0){
            sons[par[u]] += sons[u];
        }
    }
    //剖分链
    l = r = 0;
    que[r++] = tr;
    ln = cnt = 0;
    while(l != r){
        u = que[l++];
        st[ln] = dep[u]; //用节点的深度作为线段树中区间的左右标号
        top[ln] = u;
        while(u >= 0){
            id[u] = ln;
            ed[ln] = dep[u];
            seg[dep[u]] = u;
            int best;
            for(cur = fir[u], best=-1; cur; cur = cur->nxt){
                if(id[v = cur->v] == -1){
                    if(best == -1 || (best >= 0 && sons[v] > sons[best])){
                        best = v;
                    }
                }
            }
            if(best >= 0){
                for(cur = fir[u]; cur; cur = cur->nxt){
                    if(id[v = cur->v] == -1 && best != v){
                        que[r++] = v;
                    }
                }
            }
            u = best;
        }
        root[ln] = -1;
        build(root[ln], st[ln], ed[ln]);
        ln++;
    }
}
void lqry(int& id, int ql, int qr){
    if(id == -1) return ;
    if(ql <= nodes[id].l  && nodes[id].r <= qr){
        return ;
    }
    if(nodes[id].l == nodes[id].r){
        return ;
    }
    int mid = (nodes[id].l+nodes[id].r)>>1;
    if(ql <= mid){
       lqry(nodes[id].ls, ql, qr);
    }
    if(qr > mid){
       lqry(nodes[id].rs, ql, qr);
    }
}
void qry(int u, int v){ //查询u和v之间的最大值
	while(id[u] != id[v]){
		if(id[u] > id[v]){
			swap(u, v);
		}
        int b = id[v];
        lqry(root[b], st[b], dep[v]);
        v = par[top[b]];
    }
    if(dep[u] > dep[v]){
        swap(u, v);
    }
    lqry(root[id[u]], dep[u], dep[v]);
}
int main(){
	return 0;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值