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
<
=
1
0
5
M
<
=
1
0
5
N<=10^5 M<=10^5
N<=105M<=105
0
<
=
D
i
,
V
<
2
31
,
1
<
=
L
<
=
R
<
=
N
,
1
<
=
U
<
=
N
0<=Di,V<2^{31},1<=L<=R<=N,1<=U<=N
0<=Di,V<231,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
分析:
我们设
s
[
i
]
s[i]
s[i]表示以
i
i
i为根的子树的权值和,这个可以用树状数组去维护。
但是题目要求
∑
i
=
l
r
s
[
i
]
\sum_{i=l}^{r}s[i]
∑i=lrs[i],显然枚举一遍跑树状数组是不理智的,我们可以预处理一些东西。
我们可以分块维护这个和。对于一次修改操作,我们要修改每一个块,而这个块修改的权值有一个系数,这个系数是修改点的父亲(包括自己)在这个块内的个数,这个可以dfs时候做出来。然后顺带修改一下树状数组。
那么对于询问,整块内的已经预处理了,散块直接树状数组暴力求即可。
一开始边数没有乘
2
2
2,然后还有这题要开unsigned long long
代码:
/**************************************************************
Problem: 4765
User: liangzihao
Language: C++
Result: Accepted
Time:9164 ms
Memory:147724 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL unsigned long long
const int maxn=1e5+7;
using namespace std;
int n,m,root,cnt,x,y,num,op;
int ls[maxn],a[maxn],dfn[maxn],last[maxn],f[maxn][350],l[maxn],r[maxn],bel[maxn];
LL c[maxn],sum[350];
struct edge{
int y,next;
}g[maxn*2];
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
void dfs(int x,int fa)
{
dfn[x]=last[x]=++cnt;
for (int i=1;i<=num;i++) f[x][i]=f[fa][i];
f[x][bel[x]]++;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
dfs(y,x);
last[x]=max(last[x],last[y]);
}
}
void updata(int x,int k)
{
for (int i=x;i<=n;i+=i&(-i)) c[i]+=(LL)k;
}
LL calc(int x)
{
LL sum=0;
for (int i=x;i>0;i-=i&(-i)) sum+=c[i];
return sum;
}
LL getsum(int l,int r)
{
return calc(r)-calc(l-1);
}
void build_block()
{
int block=trunc(sqrt(n));
num=n/block+(n%block!=0);
for (int i=1;i<=num;i++)
{
l[i]=(i-1)*block+1;
r[i]=i*block;
}
r[num]=n;
for (int i=1;i<=n;i++) bel[i]=(i-1)/block+1;
}
void change(int x,int k)
{
updata(dfn[x],k);
for (int i=1;i<=num;i++) sum[i]+=(LL)f[x][i]*k;
}
LL query(int x,int y)
{
LL ans=0;
if (bel[x]==bel[y])
{
for (int i=x;i<=y;i++) ans+=getsum(dfn[i],last[i]);
}
else
{
for (int i=bel[x]+1;i<=bel[y]-1;i++) ans+=sum[i];
for (int i=x;i<=r[bel[x]];i++) ans+=getsum(dfn[i],last[i]);
for (int i=l[bel[y]];i<=y;i++) ans+=getsum(dfn[i],last[i]);
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
if (x)
{
add(x,y);
add(y,x);
}
else root=y;
}
build_block();
cnt=0;
dfs(root,0);
for (int i=1;i<=n;i++) change(i,a[i]);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&op,&x,&y);
if (op==1)
{
change(x,y-a[x]);
a[x]=y;
}
else printf("%llu\n",query(x,y));
}
}