BZOJ 4765: 普通计算姬 分块+树状数组

4765: 普通计算姬

Time Limit: 30 Sec   Memory Limit: 256 MB
Submit: 1461   Solved: 317
[ 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


#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<math.h>
using namespace std;
const int maxm = 100010;
const int maxn = 320;
#define ll unsigned long long
int cnt, n, m, block, magic;
int L[maxm], R[maxm], s[maxm], b[maxm], f[maxm][maxn];
ll bit[maxm << 1], w[maxm], sum[maxn];
vector<int>v[maxm];
int lowbit(int k)
{
	return k&-k;
}
ll query(int x)
{
	ll ans = 0;
	for (ll i = x; i > 0; i -= lowbit(i))
		ans += bit[i];
	return ans;
}
void change(int x, ll val)
{
	for (int i = x;i <= n;i += lowbit(i))
		bit[i] += val;
}
void dfs(int k, int pre)
{
	s[b[k]]++;
	L[k] = ++cnt;
	for (int i = 1;i <= block;i++) f[k][i] = s[i];
	change(L[k], w[k]);
	for (int i = 0;i < v[k].size();i++)
	{
		if (v[k][i] == pre) continue;
		dfs(v[k][i], k);
	}
	R[k] = cnt;
	s[b[k]]--;
}
int main()
{
	int i, j, k, t, root, ord;
	ll x, y;
	scanf("%d%d", &n, &m);
	for (i = 1;i <= n;i++) scanf("%llu", &w[i]);
	magic = (int)sqrt(n);
	block = (n - 1) / magic + 1;
	for (i = 1;i <= n;i++) b[i] = (i - 1) / magic + 1;
	for (i = 1;i <= n;i++)
	{
		scanf("%llu%llu", &x, &y);
		if (x == 0) root = y;
		else v[x].push_back(y), v[y].push_back(x);
	}
	dfs(root, -1);
	for (i = 1;i <= n;i++)
		sum[b[i]] += query(R[i]) - query(L[i] - 1);
	while (m--)
	{
		scanf("%d%llu%llu", &ord, &x, &y);
		if (ord == 1)
		{
			change(L[x], y - w[x]);
			for (i = 1;i <= block;i++) sum[i] += (ll)(f[x][i] * (y - w[x]));
			w[x] = y;
		}
		else
		{
			ll ans = 0;
			if (b[x] == b[y])
			{
				for (i = x;i <= y;i++)
					ans += query(R[i]) - query(L[i] - 1);
			}
			else
			{
				for (i = b[x] + 1;i < b[y];i++) ans += sum[i];
				for (i = x;i <= n&&b[i] == b[x];i++) ans += query(R[i]) - query(L[i] - 1);
				for (i = y;b[i] == b[y] && i;i--) ans += query(R[i]) - query(L[i] - 1);
			}
			printf("%llu\n", ans);
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值