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

Posted fcwww

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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(n\sqrt n)$
修改时对所有块直接用刚才预处理出来的打标记,同时也要在树状数组上修改出来。
查询时整块直接加上标记,零散的在树状数组上暴力。
总时间复杂度$O(n\sqrt n logn)$,可过。
其实也可以对那些零散的修改$O(\sqrt n)$查询$O(1)$,能做到$O(n\sqrt 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\n",query(x,y));
        }
    }
}

 

以上是关于BZOJ_4765_普通计算姬_分块+dfs序+树状数组的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4765普通计算姬(双重分块)

bzoj4765: 普通计算姬 (分块 && BIT)

bzoj4765 普通计算姬

[BZOJ4765]普通计算姬

bzoj [ 2017省队十连测推广赛1 ] ( 4765 && 4766 && 4767 )题解

BZOJ_3252_攻略_线段树+dfs序