题目地址
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;
}