• BZOJ_4765_普通计算姬_分块+dfs序+树状数组


    BZOJ_4765_普通计算姬_分块

    Description

    "奋战三星期,造台计算机"。小G响应号召,花了三小时造了台普通计算姬。普通计算姬比普通计算机要厉害一些
    。普通计算机能计算数列区间和,而普通计算姬能计算树中子树和。更具体地,小G的计算姬可以解决这么个问题
    :给定一棵n个节点的带权树,节点编号为1到n,以root为根,设sum[p]表示以点p为根的这棵子树中所有节点的权
    值和。计算姬支持下列两种操作:
    1 给定两个整数u,v,修改点u的权值为v。
    2 给定两个整数l,r,计算sum[l]+sum[l+1]+....+sum[r-1]+sum[r]
    尽管计算姬可以很快完成这个问题,可是小G并不知道它的答案是否正确,你能帮助他吗?

    Input

    第一行两个整数n,m,表示树的节点数与操作次数。
    接下来一行n个整数,第i个整数di表示点i的初始权值。
    接下来n行每行两个整数ai,bi,表示一条树上的边,若ai=0则说明bi是根。
    接下来m行每行三个整数,第一个整数op表示操作类型。
    若op=1则接下来两个整数u,v表示将点u的权值修改为v。
    若op=2则接下来两个整数l,r表示询问。
    N<=10^5,M<=10^5
    0<=Di,V<2^31,1<=L<=R<=N,1<=U<=N

    Output

    对每个操作类型2输出一行一个整数表示答案。

    Sample Input

    6 4
    0 0 3 4 0 1
    0 1
    1 2
    2 3
    2 4
    3 5
    5 6
    2 1 2
    1 1 1
    2 3 6
    2 3 5

    Sample Output

    16
    10
    9

    对结点编号进行分块,树状数组维护dfs序。
    先预处理出来i到根路径上经过了多少第j块内的点,预处理时间复杂度$O(nsqrt n)$
    修改时对所有块直接用刚才预处理出来的打标记,同时也要在树状数组上修改出来。
    查询时整块直接加上标记,零散的在树状数组上暴力。
    总时间复杂度$O(nsqrt n logn)$,可过。
    其实也可以对那些零散的修改$O(sqrt n)$查询$O(1)$,能做到$O(nsqrt n)$不过我写不过带log的。
     
    代码:
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <math.h>
    using namespace std;
    #define N 100050
    typedef unsigned long long ll;
    int size,block,L[N],R[N],pos[N],n,m;
    int dep[N],fa[N],head[N],to[N<<1],nxt[N<<1],val[N],cnt,root,poi[N][350],dfn[N],son[N];
    ll sum[N],tag[N],s[N],c[N];
    inline void add(int u,int v) {
        to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    void fix(int x,ll v) {
        for(;x<=n;x+=x&(-x)) c[x]+=v;
    }
    ll inq(int x) {
        ll re=0;
        for(;x;x-=x&(-x)) re+=c[x]; return re;
    }
    void dfs(int x,int y) {
        int i,j; fa[x]=y; dep[x]=dep[y]+1; sum[x]=val[x]; dfn[x]=++dfn[0];
        poi[x][pos[x]]++;
        for(i=head[x];i;i=nxt[i]) {
            if(to[i]!=y) {
                for(j=1;j<=block;j++) poi[to[i]][j]=poi[x][j];
                dfs(to[i],x);
                sum[x]+=sum[to[i]];
            }
        }
        son[x]=dfn[0];
    }
    void modify(int x,int y) {
        int i;
        for(i=1;i<=block;i++) tag[i]+=1ll*y*poi[x][i];
    }
    ll query(int x,int y) {
        int p=pos[x],q=pos[y],i;
        ll ans=0;
        if(p==q) {
            for(i=x;i<=y;i++) ans+=inq(son[i])-inq(dfn[i]-1);
        }else {
            for(i=p+1;i<q;i++) ans+=s[i]+tag[i];
            for(i=x;i<=R[p];i++) {
                ans+=inq(son[i])-inq(dfn[i]-1);
            }
            for(i=L[q];i<=y;i++) {
                ans+=inq(son[i])-inq(dfn[i]-1);
            }
        }
        return ans;
    }
    int main() {
        scanf("%d%d",&n,&m);
        int i,x,y,opt,j;
     
        size=sqrt(n);
        block=n/size;
        for(i=1;i<=block;i++) {
            L[i]=R[i-1]+1; R[i]=size*i;
            for(j=L[i];j<=R[i];j++) {
                pos[j]=i;
            }
        }
        if(R[block]!=n) {
            block++; L[block]=R[block-1]+1; R[block]=n;
            for(i=L[block];i<=n;i++) pos[i]=block;
        }
     
        for(i=1;i<=n;i++) scanf("%d",&i[val]);
        for(i=1;i<=n;i++) {
            scanf("%d%d",&x,&y);
            if(x) {
                add(x,y); add(y,x);
            }else root=y;
        }
        dfs(root,0);
        for(i=1;i<=n;i++) s[pos[i]]+=sum[i],fix(dfn[i],val[i]);
        while(m--) {
            scanf("%d%d%d",&opt,&x,&y);
            if(opt==1) {
                modify(x,-val[x]); fix(dfn[x],-val[x]);val[x]=y;
                modify(x,val[x]); fix(dfn[x],val[x]);
            }else {
                printf("%llu
    ",query(x,y));
            }
        }
    }
    
  • 相关阅读:
    PHP抛出简单说明
    html设置强制缓存的方法
    php yield处理大数据的方法
    pixijs 粒子聚合图片
    PHP监听消息队列的方法
    C++ fstream 二进制读写文件 (一个文件备份的例子)
    Ubuntu18.04下Docker CE安装
    Python3.9安装
    如何通过SQL命令更改Postgres的max_connections
    ubuntu 配置br0网桥,亲测有效
  • 原文地址:https://www.cnblogs.com/suika/p/9062658.html
Copyright © 2020-2023  润新知