題面
傳送門
題目大意:
給定一棵樹,每個點都有權值,邊的長度均爲1,有兩種操作
操作1:將節點u的值增加x,並且對於u的子樹中的任意一個點v,將它的值增加x-dist(u,v)*k, dist(u,v)表示u,v之間的距離
操作2:查詢節點u的值
分析
這類題目需要用到一個重要的思想:將樹上操作轉化爲區間操作
通過DFS序我們可以實現這一點.
對於每個節點x,我們記錄它在前序遍歷中的位置l[x],再一次回到x時的序號r[x],則x及其子樹的區間爲前序遍歷中的[l[x],r[x]]
如:
這棵樹的前序遍歷爲0 1 4 5 2 6 3 7 8 9 10
後序遍歷爲4 5 1 6 2 7 8 9 10 3 0
對於點3來說,它在前序遍歷中的序號爲7,遍歷完7,8,9後回到3的序號爲10,則區間爲[7,10]
將樹上操作轉化爲區間之後,我們處理兩種操作
顯然是用線段樹的區間修改和單點查詢實現
每次修改時,對於u的後代v,我們發現它增加的值 (d[x]表示x的深度)
其中,對於u的每個後代v, 都是一樣的,可以批量修改,而 則由每個節點決定
因此,我們用兩棵線段樹維護
一棵維護 ,一棵維護
修改時,我們把 和 分別累加到區間[l[u],r[u]]中每一個點
查詢時,我們可以求出每個節點的 的總和a,以及 的總和b
答案就是
時間複雜度
代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 300005
#define mod 1000000007ll
using namespace std;
int n,q;
struct edge{
int from;
int to;
int next;
}E[maxn<<1];
int size;
int head[maxn];
void add_edge(int u,int v){
size++;
E[size].from=u;
E[size].to=v;
E[size].next=head[u];
head[u]=size;
}
struct segment_tree{
struct node{
int l;
int r;
long long mark;
long long v;
}tree[maxn<<2];
segment_tree(){
memset(tree,0,sizeof(tree));
}
void build(int l,int r,int pos){
tree[pos].l=l;
tree[pos].r=r;
tree[pos].mark=0;
tree[pos].v=0;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
if(tree[pos].mark){
tree[pos<<1].mark=(tree[pos].mark+tree[pos<<1].mark)%mod;
tree[pos<<1|1].mark=(tree[pos].mark+tree[pos<<1|1].mark)%mod;
tree[pos<<1].v=(tree[pos].mark+tree[pos<<1].v)%mod;
tree[pos<<1|1].v=(tree[pos].mark+tree[pos<<1|1].v)%mod;
tree[pos].mark=0;
}
}
void update(int L,int R,long long v,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
tree[pos].v=(tree[pos].v+v)%mod;
tree[pos].mark=(tree[pos].mark+v)%mod;
return ;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
if(L<=mid) update(L,R,v,pos<<1);
if(R>mid) update(L,R,v,pos<<1|1);
return;
}
long long query(int L,int R,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
return tree[pos].v;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
long long ans=0;
if(L<=mid) ans=(ans+query(L,R,pos<<1))%mod;
if(R>mid) ans=(ans+query(L,R,pos<<1|1))%mod;
return ans;
}
};
segment_tree T1,T2;
int l[maxn],r[maxn];
int deep[maxn];
int cnt=0;
void dfs(int x,int fa){
l[x]=++cnt;
deep[x]=deep[fa]+1;
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs(y,x);
}
}
r[x]=cnt;
}
int main(){
int p,cmd;
int v,x,k;
scanf("%d",&n);
int root;
for(int i=2;i<=n;i++){
scanf("%d",&p);
add_edge(i,p);
add_edge(p,i);
}
dfs(1,0);
T1.build(1,n,1);
T2.build(1,n,1);
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d",&cmd);
if(cmd==1){
scanf("%d %d %d",&v,&x,&k);
T1.update(l[v],r[v],(long long)x+(long long)deep[v]*k%mod,1);
T2.update(l[v],r[v],(long long)k,1);
}else{
scanf("%d",&v);
long long ans=(T1.query(l[v],l[v],1)-(long long)deep[v]*T2.query(l[v],l[v],1)+mod)%mod;
if(ans<0) ans+=mod;
printf("%I64d\n",ans%mod);
}
}
}