「BZOJ4765」普通计算姬【DFS序+树状数组+分块】

4765: 普通计算姬

Time Limit: 30 Sec Memory Limit: 256 MB
Submit: 2150 Solved: 497

Description

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

Input

第一行两个整数 n , m n,m n,m,表示树的节点数与操作次数。
接下来一行 n n n个整数,第i个整数 d i d_i di表示点 i i i的初始权值。
接下来 n n n行每行两个整数 a i , b i a_i,b_i ai,bi,表示一条树上的边,若 a i = 0 a_i=0 ai=0则说明 b i b_i bi是根。
接下来 m m m行每行三个整数,第一个整数 o p op op表示操作类型。
o p = 1 op=1 op=1则接下来两个整数 u , v u,v u,v表示将点u的权值修改为 v v v
o p = 2 op=2 op=2则接下来两个整数 l , r l,r l,r表示询问。
N ≤ 1 0 5 , M &lt; = 1 0 5 N\leq 10^5,M&lt;=10^5 N105,M<=105
0 ≤ d i , V &lt; 2 31 , 1 ≤ L ≤ R ≤ N , 1 ≤ U ≤ N 0\leq d_i,V&lt;2^{31},1\leq L\leq R\leq N,1\leq U\leq N 0di,V<231,1LRN,1UN

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

题意

  • 就是给你一颗带有点权的树,然后需要支持两种操作:
    1   u   v 1\ u\ v 1 u v:修改节点u的权值为v
    2   l   r 2\ l\ r 2 l r:查询 ∑ i = l r 以 节 点 i 为 根 的 子 树 的 权 值 和 \sum_{i=l}^{r}{以节点i为根的子树的权值和} i=lri

题解

  • 显然暴力做法就是建 d f s dfs dfs序,然后用树状数组维护一下前缀和,这样更新复杂度为 O ( log ⁡ n ) O(\log n) O(logn),查询复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),考虑均摊一下这个复杂度,即将所有节点分个块,然后考虑对于任意节点 i i i对每一个块的影响(即影响的节点个数),这个可以用一遍 d f s dfs dfs跑出来,然后通过维护块内所有节点的子树权值和,可以做到整块 O ( 1 ) O(1) O(1)查询,然后非整块可以直接用树状数组维护 d f s dfs dfs序的权值前缀和,然后暴力算出非整块的每一个节点的子树权值和,这样复杂度为 O ( n n log ⁡ n ) O(n \sqrt n \log n) O(nn logn)
  • 注意会爆 l o n g   l o n g long\ long long long

代码

/**************************************************************
    Problem: 4765
    User: wzw1105
    Language: C++
    Result: Accepted
    Time:11568 ms
    Memory:168720 kb
****************************************************************/
#include<bits/stdc++.h>
 
using namespace std;
const int maxn=1e5+10;
vector<int>vec[maxn];
int a[maxn],root,tim=0,in[maxn],out[maxn],relate[maxn][400],n,q,sta[400];
 
namespace bit{
    unsigned long long s[maxn];  //维护dfs序的权值前缀和
    int lowbit(int x) {return x&(-x);} 
    void add(int id,unsigned long long val){
        for(int i=id;i<=n;i+=lowbit(i)){  //注意这里的n,需要根据题目要求改
            s[i]+=val;
        }
    }
    unsigned long long query(int id){
        unsigned long long ans=0;
        for(int i=id;i>=1;i-=lowbit(i)){
            ans+=s[i];
        }
        return ans;
    }
    unsigned long long sum(int l,int r){
        return query(r)-query(l-1);
    }
    void clear(){memset(s,0,sizeof(s));}
};
using namespace bit;
 
namespace blk{
    unsigned long long val[400];
    int block,num,belong[maxn];
    void init_block(int n) {
        block=sqrt(n);
        num=n%block?n/block+1:n/block;
        for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
    }
};
using namespace blk;
 
void dfs(int cur,int fa)
{
    sta[belong[cur]]++;in[cur]=++tim;
    for(int i=1;i<=num;i++) relate[cur][i]=sta[i];
    for(int i=0;i<vec[cur].size();i++) if(vec[cur][i]!=fa) dfs(vec[cur][i],cur);
    sta[belong[cur]]--;out[cur]=tim;
}
 
void update(int pos,int v)
{
    for(int i=1;i<=num;i++) val[i]+=(unsigned long long)relate[pos][i]*v; //每个块的和更新
    add(in[pos],v);
}
 
unsigned long long query(int l,int r) 
{
    unsigned long long res=0;
    if(belong[r]-belong[l]<=1) {
        for(int i=l;i<=r;i++) res+=sum(in[i],out[i]);
        return res;
    }
    for(int i=l;i<=belong[l]*block;i++) res+=sum(in[i],out[i]);
    for(int i=belong[l]+1;i<=belong[r]-1;i++) res+=val[i];
    for(int i=(belong[r]-1)*block+1;i<=r;i++) res+=sum(in[i],out[i]);
    return res;
}
 
int main()
{
    scanf("%d %d",&n,&q);
    init_block(n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1,u,v;i<=n;i++) {
        scanf("%d %d",&u,&v);
        if(!u) root=v;
        else   vec[u].push_back(v),vec[v].push_back(u);
    }
    dfs(root,0);
    for(int i=1;i<=n;i++) update(i,a[i]);
    for(int i=1,opt,l,r;i<=q;i++) {
        scanf("%d %d %d",&opt,&l,&r);
        if(opt==1) {
            update(l,r-a[l]);
            a[l]=r;
        }else {
            printf("%llu\n",query(l,r));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值