bzoj4765: 普通计算姬 分块 dfs序+线段树

bzoj4765: 普通计算姬

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序+BIT
这个时候每个修改是 O ( l o g n ) 的,每个询问是 O ( n l o g n )
用分块来平衡修改和询问。
注意到一个数的修改相当于把这个数到根节点的路径上的所有点加上权值。
于是我们可以一遍 D F S 搞出每个点对各个块的影响。
具体地,定义 f [ i ] [ j ] 为第i个点修改会影响到第j个块中的多少个点。
操作就是开个桶进栈出栈的时候统计一下。
这个过程是 O ( n m ) 的,其中 m 是块的大小。
询问的话分为不完整块和完整块即可。
复杂度 O ( n m + m l o g n )
理论上 m = n l o g n 比较优秀。
但是空间好像不是很行的样子,对这题来说 O ( n n l o g n ) 很优秀
其实是我懒得算
上代码吧。

代码

#include<cstdio>
#include<cmath>
const int N = 1e5 + 10, M = 317;
typedef unsigned long long ULL;
int ri() {
    char ch = getchar(); int x = 0;
    for(;ch < '0' || ch > '9'; ch = getchar()) ;
    for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch;
    return x;
}
int a[N], in[N], out[N], w[N], b[N], f[N][M], pr[N], nx[N << 1], to[N << 1], m, tot, blo, tp, n;
ULL sum[M], t[N];
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u);}
void Dfs(int u, int fa) {
    ++a[b[u]]; in[u] = ++tot;
    for(int i = 1;i <= m; ++i) f[u][i] = a[i];
    for(int i = pr[u]; i; i = nx[i])
    if(to[i] != fa) Dfs(to[i], u);
    --a[b[u]]; out[u] = tot;
}
void TA(int i, int v) {for(;i <= n; i += i&-i) t[i] += v;}
ULL TQ(int i) {ULL r = 0; for(;i; i -= i&-i) r += t[i]; return r;}
void Change(int u, int w) {for(int i = 1;i <= m; ++i) sum[i] += (ULL)f[u][i] * w; TA(in[u], w);}
void Query(int l, int r) {
    ULL ret = 0;
    if(b[r] - b[l] <= 1) {
        for(int i = l;i <= r; ++i) ret += TQ(out[i]) - TQ(in[i] - 1);
        return void(printf("%llu\n", ret));
    }
    for(int i = b[l] + 1; i <= b[r] - 1; ++i) ret += sum[i];
    for(int i = l; i <= b[l] * blo; ++i) ret += TQ(out[i]) - TQ(in[i] - 1);
    for(int i = (b[r] - 1) * blo + 1;i <= r; ++i) ret += TQ(out[i]) - TQ(in[i] - 1);
    printf("%llu\n", ret);
}
int main() {
    n = ri(); int q = ri(), rt; blo = sqrt(n); m = (n - 1) / blo + 1;
    for(int i = 1;i <= n; ++i) b[i] = (i - 1) / blo + 1;
    for(int i = 1;i <= n; ++i) w[i] = ri();
    for(int i = 1;i <= n; ++i) {
        int u = ri(), v = ri(); 
        if(!u) rt = v; else adds(u, v);
    }
    Dfs(rt, 0);
    for(int i = 1;i <= n; ++i) Change(i, w[i]);
    for(;q--;) {
        int op = ri(), x = ri(), y = ri();
        if(op == 1) Change(x, y - w[x]), w[x] = y;
        if(op == 2) Query(x, y);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/80524046
今日推荐