Codeforces Round 232 Div 1 C On Changing Tree 树状数组 或 线段树

本文介绍了一种利用树状数组解决特定括号序列问题的方法,通过将相对位置转换为绝对位置,并采用两棵树状数组分别记录增量和深度变化,实现了高效的区间更新与查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址

http://codeforces.com/problemset/problem/396/C

树状数组,对于搞括号序列还是有一手的,关键是因为括号序列的题老是逮着某个点询问,而他的更新又往往是涉及一棵子树。

当然,树状数组的更新也要满足可加性的。

对于向某棵子树上的节点均增加 X 这种操作,我们可以在括号序列的左端点增加x,右端点+1处增加 -x,这样使用 sum 查询到内部的时候就会增加x,并且不会影响外部的节点。

但是这个题,每个节点增加的并不是相同的数值,而是一个和被改变节点相对位置有关的值。

对于这种相对位置,我们可以改成绝对位置。

比如 求某段区间的和,我们可以预先求出每个节点到0 的和,然后求左右端点的差。

这里,我们可以求出每个点的绝对深度,用 deep-deep_change 来表示相对深度、

于是

增加 x 这个操作的影响变为

x - (deep - deepChange )*k = (x + deepChange*k) - deep * k

好,对于这个式子,前面一项满足可加性,后面一项满足可加性,结束了。。。两棵BIT

 

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <map>
#include <set>
#include <cmath>

using namespace std;

#define MAX 700000 
#define INF 0x3f3f3f3f
#define MS(x) memset(x,0,sizeof(x))
#define ll long long
#define P pair<int,int>
#define fst first
#define sec second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define MOD 1000000007

int cnt=0;
void getMod(ll &num)
{
	num=num%MOD;
}
struct bit
{
	ll data[MAX];
	ll sum(int i)
	{
		ll res=0;
		while(i)
		{
			res+=data[i];
			i-=i&(-i);
			getMod(res);
		}
		return res;
	}
	void add(int i,ll x)
	{
		while(i<=cnt)
		{
			data[i]+=x;
			getMod(data[i]);
			i+=i&(-i);
		}
	}
}Sub,Sum;

int L[MAX],R[MAX],D[MAX];

vector<int> G[MAX];

void dfs(int x,int fa,int deep)
{
	D[x]=deep;
	L[x]=++cnt;
	for(int i=0;i<G[x].size();i++)
	{
		int v=G[x][i];
		if(v!=fa)
			dfs(v,x,deep+1);
	}
	R[x]=++cnt;
}
void init()
{
	for(int i=0;i<MAX;i++)
		G[i].clear();
	cnt=0;
	MS(Sum.data);
	MS(Sub.data);
}
int main()
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		init();
		for(int i=2;i<=n;i++)
		{
			int f;
			scanf("%d",&f);
			G[f].push_back(i);
		}
		dfs(1,0,0);
		int q;
		scanf("%d",&q);
		while(q--)
		{
			int t;
			scanf("%d",&t);
			if(t==1)
			{
				ll v,x,k;
				scanf("%I64d%I64d%I64d",&v,&x,&k);
				Sum.add(L[v],x+D[v]*k);
				Sum.add(R[v]+1,-x-D[v]*k);
				Sub.add(L[v],k);
				Sub.add(R[v]+1,-k);
			}
			if(t==2)
			{
				int v;
				scanf("%d",&v);
				ll a=Sum.sum(L[v]);
				ll b=Sub.sum(L[v])*D[v];
				ll ans=((a-b)%MOD+MOD)%MOD;
				cout<<ans<<endl;
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值