zoukankan      html  css  js  c++  java
  • hdu 3966 Aragorn's Story(树链剖分+区间修改+单点查询)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966

    题意:给一棵树,并给定各个点权的值,然后有3种操作:

    I C1 C2 K: 把C1与C2的路径上的所有点权值加上K

    D C1 C2 K:把C1与C2的路径上的所有点权值减去K

    Q C:查询节点编号为C的权值

    题解:就是树链剖分具体看代码,还有注释。

    #include <iostream>
    #include <cstring>
    using namespace std;
    const int M = 5e4 + 10;
    struct Edge {
        int v , next , w;
    }edge[M << 1];
    int head[M] , e;//链式前向星存边
    int top[M];//重链的根节点
    int fa[M];//fa[i]表示i的父节点
    int deep[M];//节点的深度
    int num[M];//num[i]表示i节点子树一共有多少节点
    int p[M];//p[i]表示i点在线段树中的位置
    int fp[M];//fp[i]表示线段树中位置为i的节点是什么
    int son[M];//sum[u]表示u点的重儿子
    int pos;
    void init() {
        memset(head , -1 , sizeof(head));
        memset(son , -1 , sizeof(son));
        e = 0;
        pos = 1;
    }//初始化
    void add(int u , int v) {
        edge[e].v = v;
        edge[e].next = head[u];
        head[u] = e++;
    }//加边
    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].v;
            if(v != pre) {
                dfs1(v , u , d + 1);
                num[u] += num[v];
                if(son[u] == -1 || num[v] > num[son[u]]) {
                    son[u] = v;
                }
            }
        }
    }//寻找重点
    void getpos(int u , int sp) {
        top[u] = sp;
        p[u] = pos++;
        fp[pos - 1] = u;
        if(son[u] == -1)
            return ;
        getpos(son[u] , sp);
        for(int i = head[u] ; i != -1 ; i = edge[i].next) {
            int v = edge[i].v;
            if(v != son[u] && v != fa[u]) {
                getpos(v , v);
            }
        }
    }//构建重链
    struct TnT {
        int l , r , sum , lazy;
    }T[M << 2];
    int a[M];
    void build(int l , int r , int i) {
        int mid = (l + r) >> 1;
        T[i].l = l , T[i].r = r , T[i].lazy = 0 , T[i].sum = 0;
        if(T[i].l == T[i].r) {
            T[i].sum = a[fp[l]];
            return ;
        }
        build(l , mid , i << 1);
        build(mid + 1 , r , (i << 1) | 1);
        T[i].sum = T[i << 1].sum + T[(i << 1) | 1].sum;
    }
    void pushdown(int i) {
        if(T[i].lazy) {
            T[i << 1].sum += T[i].lazy * (T[i << 1].r - T[i << 1].l + 1);
            T[(i << 1) | 1].sum += T[i].lazy * (T[(i << 1) | 1].r - T[(i << 1) | 1].l + 1);
            T[i << 1].lazy += T[i].lazy;
            T[(i << 1) | 1].lazy += T[i].lazy;
            T[i].lazy = 0;
        }
    }
    void updata(int l , int r , int ad , int i) {
        int mid = (T[i].l + T[i].r) >> 1;
        if(T[i].l == l && T[i].r == r) {
            T[i].sum += ad * (r - l + 1);
            T[i].lazy += ad;
            return ;
        }
        pushdown(i);
        if(mid < l) {
            updata(l , r , ad , (i << 1) | 1);
        }
        else if(mid >= r) {
            updata(l , r , ad , i << 1);
        }
        else {
            updata(l , mid , ad , i << 1) , updata(mid + 1 , r , ad , (i << 1) | 1);
        }
        T[i].sum = T[i << 1].sum + T[(i << 1) | 1].sum;
    }
    int query(int i , int pos) {
        int mid = (T[i].l + T[i].r) >> 1;
        if(T[i].l == pos && T[i].r == pos) {
            return T[i].sum;
        }
        pushdown(i);
        T[i].sum = T[i << 1].sum + T[(i << 1) | 1].sum;
        if(mid < pos) {
            return query((i << 1) | 1 , pos);
        }
        else {
            return query(i << 1 , pos);
        }
    }
    void change(int u , int v , int ad) {
        int f1 = top[u] , f2 = top[v];
        while(f1 != f2) {
            if(deep[f1] < deep[f2]) {
                swap(f1 , f2);
                swap(u , v);
            }
            updata(p[f1] , p[u] , ad , 1);
            u = fa[f1] , f1 = top[u];
        }
        if(deep[u] < deep[v])
            swap(u , v);
        updata(p[v] , p[u] , ad , 1);
    }//树链剖分特别的更新方法,类似于lca从各子链上查询直到到了同一条重链上自行理解一下。
    int main() {
        int n , m , q , x , y , z;
        while(scanf("%d%d%d" , &n , &m , &q) != EOF) {
            init();
            for(int i = 1 ; i <= n ; i++) {
                scanf("%d" , &a[i]);
            }
            for(int i = 0 ; i < m ; i++) {
                scanf("%d%d" , &x , &y);
                add(x , y);
                add(y , x);
            }
            dfs1(1 , 0 , 0);
            getpos(1 , 1);
            char cp[5];
            build(0 , pos + 1 , 1);
            while(q--) {
                scanf("%s" , cp);
                if(cp[0] == 'I') {
                    scanf("%d%d%d" , &x , &y , &z);
                    change(x , y , z);
                }
                if(cp[0] == 'D') {
                    scanf("%d%d%d" , &x , &y , &z);
                    change(x , y , -z);
                }
                if(cp[0] == 'Q') {
                    scanf("%d" , &x);
                    printf("%d
    " , query(1 , p[x]));
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Spark Scala 读取GBK文件的方法
    Mac OS X 系统下自带的文本文件格式转换工具iconv
    报到
    java 字符串中含有双引号" "与单引号' '问题
    div1嵌套div2,div2居中的解决办法
    ionic4 创建 angular项目 ReactNative下载第三方库出错解决Error: EPERM: operation not permitted, rename
    ionic+cordova 创建项目+打包
    jxl读取excel文件异常:Unable to recognize OLE stream 的解决方法
    学习 javascript (一)javascript 简介
    学习 JavaScript (四)核心概念:操作符
  • 原文地址:https://www.cnblogs.com/TnT2333333/p/6766200.html
Copyright © 2011-2022 走看看