ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树链剖分(待续)

2019-05-16 12:48:39  阅读:149  来源: 互联网

标签:待续 剖分 int top pos 树链 Maxn include 节点


时间复杂度:

预处理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函数

 

 

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

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

 

基本例题:

洛谷P3386(点权)

操作类型:

 

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);
        }
    }
}

 

 

 

 

标签:待续,剖分,int,top,pos,树链,Maxn,include,节点
来源: https://blog.csdn.net/castomere/article/details/90261061

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有