4765: 普通计算姬
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 1481 Solved: 318
[ Submit][ Status][ Discuss]
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
令sum[x]表示以x为根的子树权值和
而求出每个节点的dfs序之后,属于同一棵子树的所有节点一定是连续的,这样sum[x]就可以用树状数组logn查询
考虑每次询问l, r暴力sum[l]到sum[r]:
预处理复杂度:O(nlogn),每次查询:O(nlogn),修改O(logn)
修改复杂度挺低的,但查询复杂度过高,考虑将sum[]分块
首先每修改一个点都可能对多个sum[]造成影响,所以要先nsqrt(n)预处理每个节点修改之后对每一块的影响
然后对于每次询问l, r,暴力每一块,如果当前块完全在区间[l, r]内直接加上块的权值,如果不是就树状数组暴力求sum[]然后再加在一起
修改的话就是看当前点对每一块的权值贡献,然后单点更新树状数组
预处理复杂度:O(nsqrt(n)),每次查询:O(nlognsqrt(n)),修改O(sqrt(n))
其实还有更快的方法,就是对dfs序数列也分块,但常数过大并没有快多少
大概是8000ms优化到5500ms
#include<stdio.h>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;
#define LL unsigned long long
vector<int> G[100005];
int n, cnt, dfn[100005], End[100005];
int B, val[100005], bel[100005], L[100005], R[100005], giv[100005][322];
LL tre[100005], sb[322], temp[322];
int Read()
{
int x = 0, f = 1;
char ch;
ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
x = x*10+ch-'0', ch = getchar();
return x*f;
}
void Update(int k, int sv)
{
while(k<=n)
{
tre[k] += sv;
k += k&-k;
}
}
LL Query(int k)
{
LL now = 0;
while(k)
{
now += tre[k];
k -= k&-k;
}
return now;
}
void Sech(int u, int p)
{
int i, v;
dfn[u] = ++cnt;
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
if(v==p)
continue;
Sech(v, u);
}
End[u] = cnt;
}
void Sech2(int u, int p)
{
int i, v;
temp[bel[u]]++;
for(i=1;i<=B;i++)
giv[u][i] = temp[i];
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
if(v==p)
continue;
Sech2(v, u);
}
temp[bel[u]]--;
}
int main(void)
{
LL ans;
//freopen("in.txt", "r", stdin);
//freopen("out2.txt", "w", stdout);
int q, root, i, x, y, opt, block, j, ly;
scanf("%d%d", &n, &q);
for(i=1;i<=n;i++)
val[i] = Read();
for(i=1;i<=n;i++)
{
x = Read(), y = Read();
if(x==0)
root = y;
else
{
G[x].push_back(y);
G[y].push_back(x);
}
}
Sech(root, 0);
for(i=1;i<=n;i++)
Update(dfn[i], val[i]);
y = 1, x = 0;
block = sqrt(n)+1;
for(i=1;i<=n;i++)
{
x++;
if(x==1) L[y] = i;
if(x==block) R[y] = i;
bel[i] = y;
sb[y] += Query(End[i])-Query(dfn[i]-1);
if(x==block)
x = 0, y++;
}
if(x==0)
y--;
else
R[y] = i-1;
B = y;
Sech2(root, 0);
while(q--)
{
opt = Read(), x = Read(), y = Read();
if(opt==1)
{
for(i=1;i<=B;i++)
sb[i] += (LL)(y-val[x])*giv[x][i];
Update(dfn[x], y-val[x]);
val[x] = y;
}
else
{
ans = 0;
for(i=1;i<=B;i++)
{
if(L[i]>=x && R[i]<=y)
ans += sb[i];
else if(x>L[i] && x<=R[i])
{
ly = min(R[i], y);
for(j=x;j<=ly;j++)
ans += Query(End[j])-Query(dfn[j]-1);
}
else if(y>=L[i] && y<R[i])
{
for(j=L[i];j<=y;j++)
ans += Query(End[j])-Query(dfn[j]-1);
break;
}
}
printf("%llu\n", ans);
}
}
}
/*
1 3
2
0 1
2 1 1
1 1 5
2 1 1
*/