ICode9

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

BZOJ p1036 树的统计(树链剖分)

2019-07-28 22:53:37  阅读:228  来源: 互联网

标签:剖分 int res top tu p1036 deep 树链 seg


树链剖分

对于一棵树上两个节点所构成的链的操作,我们可以用树链剖分,来将树转化为多条链的集合(线性结构),从而将树上链的结构转化为线性结构的区间操作.

  1. 找出每个节点的重儿子(包含节点最多的儿子)
  2. 重儿子优先输出dfs序

对于如下一棵树进行剖分

m

找出其重儿子(红色线)

每个节点与其重儿子递归组成一条链(叶子节点没有重儿子)

树链剖分后的序列为: \(0,1,2,6,3,5,4\)

现在要修改两个点路径上所有点的权值,假如两个点在一条链上,就直接修改序列的区间.否则就修改从当前点到链根的区间,然后跳到链根的父亲递归修改.

比如现在要修改\(4到5\)的所有点的权值

  • 4所在的链根为4,所以修改4到4,然后跳到4的父亲1
  • 5所在的链根为3,所以修改3到5,然后跳到3的父亲0
  • 1,0在同一条链上,直接修改1到0

所以对于树链剖分序的区间修改(dfs序的下标)为: 7-7,5-6,1-2

BZOJ 1036

  • 题意: 树上查询区间最大值,区间和,单点修改
  • 思路(好吧直接抄的代码): 先树链剖分,利用线段树维护信息和询问
#include<bits/stdc++.h>
#define ll long long 
#define FOR(i,l,r) for(int i = l ; i <= r ;++i ) 
#define inf 0x3f3f3f3f
#define EPS (1e-9)
#define ALL(T)  T.begin(),T.end()
#define lson(i)     i<<1
#define rson(i)     (i<<1|1)
using namespace std; 

const int maxn =30010;
struct Edge{
    int to,next;
}edge[maxn*2];

int head[maxn],tot; //前向星
int top[maxn];  // 所在重链的顶端节点
int fa[maxn];   // 父亲
int deep[maxn]; // 深度
int num[maxn];  // 子节点个数
int p[maxn];    // 在dfs序的位置  
int fp[maxn];   // 位置节点号的反向映射
int son[maxn]; // 重儿子
int pos;   // dfs序当前下标

// 加边
void addedge(int u,int v){
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}

// 初始化
void init(){
    memset(head,-1,sizeof(head));
    memset(son,-1,sizeof(son));
    tot = 0;
    pos = 1;
}

 //第一遍dfs   处理fa,num,deep,son
void dfs1(int u,int pre,int d){
    deep[u] = d;
    fa[u] = pre;
    num[u] = 1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        if(v!=pre){ // 所指边不是父亲
            dfs1(v,u,d+1);
            num[u] += num[v]; // 更新父亲子节点数量
            if(son[u] == -1 || num[v] > num[son[u]])
                son[u] = v; // 更新父亲重儿子
        }
    }
}
// 第二遍dfs  处理 top,p,fp
void dfs2(int u,int sp){
    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!=-1;i=edge[i].next){
        int v = edge[i].to;
        if( v!= son[u] && v!=fa[u])
            dfs2(v,v);  // 以自己为链首的新链
    }
}
// 原数组
int a[maxn];
struct node{
    int l,r;
    int sum,ma;
}seg[maxn*4];
// 线段树操作
void push_up(int p){
    seg[p].sum = seg[lson(p)].sum + seg[rson(p)].sum;
    seg[p].ma = max(seg[lson(p)].ma,seg[rson(p)].ma);
}

void build(int pp,int l,int r){
    seg[pp].l = l;
    seg[pp].r = r;
    if(l==r){
    // 这里要注意反向映射回原数组下标
        seg[pp].ma = seg[pp].sum = a[fp[l]];
        return;
    }
    int mid = (l+r)>>1;
    build(lson(pp),l,mid);
    build(rson(pp),mid+1,r);
    push_up(pp);
}

void update(int p,int l,int r,int val){
    if(seg[p].l >= l && seg[p].r <= r){
        seg[p].sum = val;
        seg[p].ma = val;
        return;
    }
    int mid = (seg[p].r+seg[p].l)>>1;
    if(l<=mid)  update(lson(p),l,r,val);
    if(r>mid)   update(rson(p),l,r,val);
    push_up(p);
}

int qmax(int p,int l,int r){
    if(seg[p].l >= l && seg[p].r <= r){
        return seg[p].ma;
    }
    int res = -inf;
    int mid = (seg[p].r+seg[p].l)>>1;
    if(l<=mid)  res = max(res,qmax(lson(p),l,r));
    if(r>mid)   res = max(res,qmax(rson(p),l,r));
    return res;
}

int qsum(int p,int l,int r){
    if(seg[p].l >= l && seg[p].r <= r){
        return seg[p].sum;
    }
    int res = 0;
    int mid = (seg[p].r+seg[p].l)>>1;
    if(l<=mid)  res = res+qsum(lson(p),l,r);
    if(r>mid)   res = res+qsum(rson(p),l,r);
    return res;
}
// 查询和
int fsum(int u,int v){
    int res = 0;
    int tu = top[u], tv = top[v];
    while(tu != tv){
        if(deep[tu]< deep[tv]){
            swap(tu,tv);
            swap(u,v);
        }
        res+= qsum(1,p[tu],p[u]);
        u = fa[tu];
        tu = top[u];
    }
    if(deep[u] > deep[v])   swap(u,v);
    res += qsum(1,p[u],p[v]);
    return res;
}
// 查询最大
int fmax(int u,int v){
    int res = -1e9;
    int tu = top[u], tv = top[v]; // u,v的链顶
    while(tu != tv){ //不在同一条链
        if(deep[tu]< deep[tv]){//先考虑较深节点
            swap(tu,tv);
            swap(u,v);
        }
        res=max(res,qmax(1,p[tu],p[u]));// 查询u节点到他的链顶
        u = fa[tu]; // 跳到链顶的父节点
        tu = top[u];// 更新链顶
    }
    if(deep[u] > deep[v])   swap(u,v);
    res = max(res,qmax(1,p[u],p[v]));// 同一条链 直接区间查询
    return res;
}


int n;
int main(){
    scanf("%d",&n);
    int fr,to;
    init();
    FOR(i,1,n-1){
        scanf("%d%d",&fr,&to);
        addedge(fr,to);
        addedge(to,fr);
    }
    dfs1(1,0,0);
    dfs2(1,1);
    FOR(i,1,n) {
        scanf("%d",&a[i]);
    }
    // FOR(i,1,n){
    //     printf("%d %d %d %d %d %d\n",top[i],fa[i],deep[i],num[i],p[i],son[i]);
    // }cout << endl;
    build(1,1,n);
    // FOR(i,1,n){
    //     update(1,i,i,a[p[i]]);
    // }
    int q;
    char op[10];
    scanf("%d",&q);
    FOR(i,1,q){
        scanf("%s%d%d",op,&fr,&to);
        if(op[0]=='C'){
            update(1,p[fr],p[fr],to);
        }else if(op[1]=='M'){
            printf("%d\n",fmax(fr,to));
        }else if(op[1]=='S'){
            printf("%d\n",fsum(fr,to));
        }
    }
    return 0;
}

题目连接
视频连接

标签:剖分,int,res,top,tu,p1036,deep,树链,seg
来源: https://www.cnblogs.com/xxrlz/p/11261337.html

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

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

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

ICode9版权所有