zoukankan      html  css  js  c++  java
  • BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)

    BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值)

    题意分析

    image

    (题目图片来自于 这里)

    第一道树链剖分的题目,谈一下自己的理解。

    树链剖分能解决的问题是,题目中反复要求对链上信息进行修改和查询。如果依旧采取用dfs序的方法,会发现不适用,原因是dfs序适用于处理子树的问题。当然暴力肯定是要被卡掉的。

    树链剖分分为两次dfs。
    第一次dfs求出每个节点的father,son,size,deep,这里涉及到重儿子的概念,网上有很多资料就不在这里赘述了。换句话说,通过第一次dfs,我们求出了树上的重儿子(size最大的那个儿子)。
    第二次dfs,就将重儿子相互连接形成重链,换句话说,求出每个节点的top(链顶),树上的每个节点,可以根据top是否相同来判断是否在一条链上,还有在线段树上的newid和线段树newid对应的实际的节点编号hashback。,因此在处理树上链修改的时候,可以很方便得进行处理。

    有了这些数据,就可以对树上每个节点建立线段树。其实newid,可以看做是树上节点到线段树叶子节点的一个hash,而数组hashback,相当于线段树叶子节点到树上节点的hash。
    通过建立的线段树,不难发现,在一链上的节点,所对应线段树区间是连续的。

    在维护线段树的时候,和普通的线段树没有区别。只是查询时,需要trick。

    这里阐述2个,查询链上最值和链上节点权值和。

    先说第一个。我们根据题目知道了两个节点编号x,y.通过两次dfs的顺序,不难得出,deep大的节点对应的newid就大(想一想为什么,可以画一画)。于是就从xy中选择一个deep比较大的,让他向上爬,每次爬选择到他的top(链顶)。为什么爬到链顶呢?原因很简单,刚才说过一句话,在一链上的节点,所对应线段树区间是连续的。,换句话说,对于这段连续的区间,就可以利用线段树的特性方便求出他的最值。 如此就能将复杂度讲的很低。但是问题又来了,如果他不在一条链上,自己很孤立,没有形成重链或者轻链,那么他就向自己的父亲爬就好了。如此一来,我们每次选择深度大的点,向上爬,一边爬一遍更新信息,知道两个人都爬到lca(x,y)时,结束。

    对于第二个,差别仅仅在于每次爬的时候用不同的方式更新信息罢了。刚才是求得最值,现在只求和,区别也仅仅在此,思路是一样的。

    至于单点修改,和线段树的修改是没有区别的,只不过修改的节点要用newid保存的值hash过去,否则会出错。

    代码总览

    #include <bits/stdc++.h>
    #define ll int
    #define nmax 30820
    using namespace std;
    int fa[nmax],son[nmax],sz[nmax],newid[nmax],hashback[nmax],dep[nmax],top[nmax];
    int num,tot,head[nmax],data[nmax];
    struct edge{
        int to;
        int next;
    }edg[nmax<<1];
    struct tree{
        int l,r,val,mx;
        int mid(){
            return (l+r)>>1;
        }
    }tree[nmax<<2];
    void add(int u, int v){
        edg[tot].to = v;
        edg[tot].next = head[u];
        head[u] = tot++;
    }
    void dfsFirst(int rt, int f,int d){
        dep[rt] = d;
        fa[rt] =f;
        sz[rt] = 1;
        for(int i = head[rt]; i!= -1; i = edg[i].next){
            int nxt = edg[i].to;
            if(nxt != f){
                dfsFirst(nxt,rt,d+1);
                sz[rt]+=sz[nxt];
                if(son[rt] == -1 || sz[nxt] > sz[son[rt]]){
                    son[rt] = nxt;
                }
            }
    
        }
    }
    void dfsSecond(int rt, int tp){
        top[rt] = tp;
        newid[rt] = ++num;
        hashback[num] = rt;
        if(son[rt] == -1) return;
        dfsSecond(son[rt],tp);
        for(int i = head[rt];i != -1; i = edg[i].next){
            int nxt = edg[i].to;
            if(nxt != son[rt] && nxt != fa[rt])
                dfsSecond(nxt,nxt);
        }
    }
    void init(){
        memset(tree,0,sizeof tree);
        memset(head,-1,sizeof head);
        memset(son,-1,sizeof son);
        memset(edg,0,sizeof edg);
        memset(hashback,0,sizeof hashback);
        tot = num = 0;
    }
    void PushUp(int rt){
        tree[rt].mx = max(tree[rt<<1].mx , tree[rt<<1|1].mx);
        tree[rt].val = tree[rt<<1].val + tree[rt<<1|1].val;
    }
    void Build(int l, int r, int rt){
        tree[rt].l = l; tree[rt].r = r;
        if(l == r){
            tree[rt].val = tree[rt].mx = data[hashback[l]];
            return;
        }
        Build(l,tree[rt].mid(),rt<<1);
        Build(tree[rt].mid()+1,r,rt<<1|1);
        PushUp(rt);
    }
    void UpdatePoint(int val, int pos, int rt){
        if(tree[rt].l == tree[rt].r){
            tree[rt].mx = tree[rt].val = val ;
            return;
        }
        if(pos <= tree[rt].mid()) UpdatePoint(val,pos,rt<<1);
        else UpdatePoint(val,pos,rt<<1|1);
        PushUp(rt);
    }
    int QueryMAX(int l,int r,int rt){
        if(l <= tree[rt].l && tree[rt].r <= r) return tree[rt].mx;
        //PushDown(rt);
        int ans = -1e9+7;
        if(l <= tree[rt].mid()) ans = max(ans,QueryMAX(l,r,rt<<1));
        if(r > tree[rt].mid()) ans = max(ans,QueryMAX(l,r,rt<<1|1));
        return ans;
    }
    int QuerySUM(int l,int r,int rt)
    {
        if(l>tree[rt].r || r<tree[rt].l) return 0;
        if(l <= tree[rt].l && tree[rt].r <= r) return tree[rt].val;
        return QuerySUM(l,r,rt<<1) + QuerySUM(l,r,rt<<1|1);
    }
    int Find_MAX(int x, int y){
        int tx = top[x],ty =top[y],ans = -1e9+7;
        while(tx != ty){
            if(dep[tx] < dep[ty]){
                swap(x,y);
                swap(tx,ty);
            }
            ans = max(ans,QueryMAX(newid[tx],newid[x],1));
            x = fa[tx]; tx = top[x];
        }
        if(dep[x] > dep[y]) ans = max(ans,QueryMAX(newid[y],newid[x],1));
        else ans = max(ans,QueryMAX(newid[x],newid[y],1));
        return ans;
    }
    int Find_SUM(int x, int y){
        int tx = top[x],ty =top[y],ans = 0;
        while(tx != ty){
            if(dep[tx] < dep[ty]){
                swap(x,y);
                swap(tx,ty);
            }
            ans += QuerySUM(newid[tx],newid[x],1);
            x = fa[tx]; tx = top[x];
        }
        if(dep[x] > dep[y]) ans += QuerySUM(newid[y],newid[x],1);
        else ans += QuerySUM(newid[x],newid[y],1);
        return ans;
    }
    
    int n,m;
    int main()
    {
        //freopen("in.txt","r",stdin);
        init();
        scanf("%d",&n);
        int u,v,m,x,y;
        char op[10];
        for(int i =1;i<=n-1;++i){
            scanf("%d %d",&u,&v);
            add(u,v);
            add(v,u);
        }
        for(int i =1;i<=n;++i) scanf("%d", &data[i]);
        dfsFirst(1,0,1);
        dfsSecond(1,1);
    //    printf("MESSA ID    DATA   FA   SON   SIZE   DEEP   NEWID  TOP
    ");
    //    for(int i = 1;i<=n;++i){
    //        printf("DEBUG %5d %5d %5d %5d %5d %5d %5d %5d
    ",i,data[i],fa[i],son[i],sz[i],dep[i],newid[i],top[i]);
    //    }
        Build(1,n,1);
    //    printf("MESSA ID   val
    ");
    //    for(int i = 1;i<=n;++i){
    //        printf("DEBUF %d   %d
    ",i,QuerySUM(i,i,1));
    //    }
        scanf("%d",&m);
        for(int i = 0;i<m;++i){
            scanf("%s %d %d",op,&x,&y);
            if(op[1] == 'S'){//QSUM
                printf("%d
    ",Find_SUM(x,y));
            }else if(op[1] == 'M'){//QMAX
                printf("%d
    ",Find_MAX(x,y));
            }else{
                UpdatePoint(y,newid[x],1);
                data[x] = y;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    夯实Java基础系列23:一文读懂继承、封装、多态的底层实现原理
    夯实Java基础系列22:一文读懂Java序列化和反序列化
    夯实Java基础系列21:Java8新特性终极指南
    夯实Java基础系列20:从IDE的实现原理聊起,谈谈那些年我们用过的Java命令
    FireDAC的SQLite初探
    delphi7 TRichView 安装
    Delphi7 GDI+学习
    Delphi7画好看的箭头线
    python swap
    pycharm **教程 大家快激活吧
  • 原文地址:https://www.cnblogs.com/pengwill/p/7367015.html
Copyright © 2011-2022 走看看