树链剖分

时间复杂度:

预处理O(n + NlogN) , 修改和查询O(logN)

树链剖分原理:

  1. 第一遍 dfs , 求得 所有节点的 深度 dep[ ] , 记录每个节点的父亲节点 fa[ ] , 记录每个节点所代表的子树的大小【包括节点本身】
  2. 第二遍 dfs , 记录 每个节点的 重链 , 记下当前节点 x  所在的链 的 链头top[x] , 每个节点在dfs序中的pos[x] , pos[x] 所对应的结点是什么 id[ pos[x] ]  = x
  3. 更新 一段 u , v 之间的 链 , 判断 u , v 的链头深度 ,向上每次 logn 的移动, 直到两个结点的链头相同 , 判断u,v当前深度,并更新
  4. 查询操作同上 , 是指改用query函数

树链剖分要点   就是通过 改变 dfs 的顺序     将DFS序 上每一段区间都表示成 一条链,就可以完成区间修改 查询操作

 

 

部分边权题简单化操作:

1.单次修改某一条边的边权 (白书POJ 2763 , P331), 不是一段 区间的边权 ,其实有更简单的做法 , 不需要应用树链剖分。

只需要跑个DFS序 , 指向叶子结点方向的边为 正 , 指向 根节点 的边 为负。

每次更新一条边的是时候,更新走过这条边 时 的正负权值 。

2.多边修改  可以进行 边权转点权  的操作 ,然后应用树链剖分。

稍加思考了一下 ,边权转点权 的过程 ,还是比较明显的。

 

树链剖分能解决的树上问题:

  • 每次选择 u , v 两点 , 对 最短路径上的【所有点进行加权 or 所有边进行加权】
  • 选择 一个点 x , 对点x和其子树上所有点进行加权
  • 计算树上 u,v 两点间 路径上的【点权和 , 边权和】
  • 查询一个点 , 和它子树上所有点 的点权和

 

基本例题:

洛谷P3384(点权)

操作类型:

 

AC代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
using namespace std;
typedef long long LL;
const int Maxn = 2e5 + 10;
const int Inf = 1e9 + 7;
int N , M , cnt , root;
int Mod;
int pos[Maxn] , top[Maxn] , sz[Maxn] , dep[Maxn] , fa[Maxn] , wson[Maxn] , id[Maxn];
vector <int> G[Maxn<<2];

//线段树部分
int A[Maxn];
struct edge{
    int l , r;
    int sum , lazy;
}tree[Maxn<<2];

void PushUp( int x ){
    tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;
}

void Build( int l , int r , int x ){
    tree[x].lazy = 0;
    tree[x].l = l , tree[x].r = r;
    if( l == r ){
        tree[x].sum = A[id[l]];
        return;
    }
    int mid = ( l + r ) >> 1;
    Build( l , mid , x << 1 );
    Build( mid + 1 , r , x<<1|1 );
    tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;
    //PushUp( x );
}

void PushDown( int x ){
    if(tree[x].lazy){
        tree[x<<1].lazy += tree[x].lazy;
        tree[x<<1|1].lazy += tree[x].lazy;
        int mid = ( tree[x].r + tree[x].l ) / 2;
        tree[x<<1].sum += ( mid - tree[x].l + 1 ) * tree[x].lazy;
        tree[x<<1|1].sum += ( tree[x].r - mid ) * tree[x].lazy;
        tree[x<<1].sum %= Mod;
        tree[x<<1|1].sum %= Mod;
        tree[x].lazy = 0;
    }
}

void Update_line( int L , int R , int add , int x ){
    if( L <= tree[x].l && tree[x].r <= R ){
        tree[x].sum += add * ( tree[x].r - tree[x].l + 1 );
        tree[x].lazy += add;
        tree[x].sum %= Mod;
        return;
    }
    PushDown( x );
    int mid = ( tree[x].l + tree[x].r ) >> 1;
    if( L <= mid )	Update_line( L , R , add , x <<1 );
    if( R > mid )	Update_line( L , R , add , x <<1|1 );
    tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;
    //PushUp( x );
}

int Query( int L , int R , int x ){
    if( L <= tree[x].l && tree[x].r <= R ){
        tree[x].sum %= Mod;
        return tree[x].sum;
    }
    PushDown( x );
    int mid = ( tree[x].l + tree[x].r ) >> 1;
    int res = 0;
    if( L <= mid )	res += Query( L , R , x<<1 );
    if( R > mid )	res += Query( L , R , x<<1| 1 );
    return res;
}


//树链剖分部分
/*
 * 第一遍dfs1
 * 计算出每个的sz ----- 每个点的子树(包括自己)的的节点个数
 * 计算出每个点的深度 dep
 * 计算出每个点的 fa 父亲节点
 * 接口 : x 是根节点 , fat是根节点的父亲节点 , dept是根节点的深度(1)
 */
void dfs1(int x , int fat , int dept){
    dep[x] = dept , fa[x] = fat , sz[x] = 1;
    int Size = G[x].size() , v;
    for(int i = 0 ; i < Size ; i++){
        v = G[x][i];
        if(v == fat)	continue;
        dfs1(v , x , dept+1);
        sz[x] += sz[v];
        if(wson[x] == -1 || sz[wson[x]] < sz[v])	wson[x] = v;
    }
}
/*
 * 第二遍dfs2
 * 计算出每个点所在重链(或者轻链)的链头结点 top[x]
 * 计算出每条边在序列中的位置 pos[x] (x到其父亲的边在序列中的位置 , 所有边组成了一个序列)
 * id[pos[x]] 记录着 边序列中 每个编号对应的 应该连着的点
 */
void dfs2(int x , int line_top){
    top[x] = line_top, pos[x] = ++cnt, id[pos[x]] = x;
    if(wson[x] == -1)	return;
    dfs2(wson[x] , line_top);
    int Size = G[x].size() , v;
    for(int i = 0 ; i < Size ; i++){
        v = G[x][i];
        if(v != fa[x] && v != wson[x])	dfs2(v , v);
    }
}

int updata0_query1(int u , int v , int c , int op){
    int ans = 0;
    while(top[u] != top[v]){
        if(dep[top[u]] > dep[top[v]])	swap(u,v);
        if(!op)	Update_line(pos[top[v]] , pos[v] , c , 1);
        else ans += Query(pos[top[v]] , pos[v] , 1) , ans %= Mod;
        v = fa[top[v]];
    }
    if(dep[u] > dep[v])	swap(u,v);
    if(op == 0)	Update_line(pos[u] , pos[v] , c , 1);
    else	ans += Query(pos[u] , pos[v] , 1) , ans %= Mod;
    return ans % Mod;
}

int main()
{
    cnt = 0;
    memset(wson , -1 , sizeof(wson));
    scanf(" %d %d %d %d",&N,&M,&root,&Mod);
    for(int i = 1 ; i <= N ; i++)	scanf(" %d",&A[i]);
    int u , v;
    for(int i = 1 ; i < N ; i++){
        scanf(" %d %d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(root , -1 , 1);
    dfs2(root , root);
    Build(1 , N , 1);
    int op , x , y , z;
    for(int i = 1 ; i <= M ; i++){
        scanf(" %d",&op);
        if(op == 1){
            scanf(" %d %d %d",&x , &y , &z);
            updata0_query1(x , y , z , 0);
        } else if(op == 2){
            scanf(" %d %d",&x , &y);
            printf("%d\n",updata0_query1(x,y,0,1));
        } else if(op == 3){
            scanf(" %d %d",&x,&z);
            Update_line(pos[x] , pos[x] + sz[x] - 1 , z , 1);
        } else if(op == 4){
            scanf(" %d",&x);
            printf("%d\n",Query(pos[x] , pos[x] + sz[x] - 1 , 1) % Mod);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值