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;
    }
    
  • 相关阅读:
    Android 解决小米手机Android Studio安装app 报错的问题It is possible that this issue is resolved by uninstalling an existi
    Android Unresolved Dependencies
    Android studio 自定义打包apk名
    Android Fragment与Activity交互的几种方式
    魅族和三星Galaxy 5.0webView 问题Android Crash Report
    Android几种常见的多渠道(批量)打包方式介绍
    Android批量打包 如何一秒内打完几百个apk渠道包
    上周热点回顾(9.30-10.6)团队
    上周热点回顾(9.23-9.29)团队
    上周热点回顾(9.16-9.22)团队
  • 原文地址:https://www.cnblogs.com/pengwill/p/7367015.html
Copyright © 2011-2022 走看看