LibreOJ-dfs序2 (dfs序,線段樹)
題目描述
給一棵有根樹,這棵樹由編號爲1~N 的 N個結點組成。根結點的編號爲R。每個結點都有一個權值,結點 的權值爲 。 接下來有 M組操作,操作分爲兩類:
1 a x,表示將結點 的子樹上所有結點的權值增加 ;
2 a,表示求結點 的子樹上所有結點的權值之和。
輸入格式
第一行有三個整數 N,M和R。
第二行有 N個整數,第 i個整數表示 vi。
在接下來的 N-1行中,每行兩個整數,表示一條邊。
在接下來的 M行中,每行一組操作。
輸出格式
對於每組 2 a操作,輸出一個整數,表示「以結點 a爲根的子樹」上所有結點的權值之和。
用到了dfs序,兩個數組 st[ ] ,en[ ] 儲存每個點遍歷的出入時間,通過時間可算出子樹節點。
通過 mp[ ] 數組,找到結點與樹的對應(映射)
用線段樹的區間修改和區間查詢,並結合 st[ ] , en[ ] 數組,找到所用的區間。
- 區間修改時標記 lazy[ ] 數組,查詢時再向下傳遞。
代碼↓
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
vector<int> q[N];
int st[N],en[N],tmp=0;
int n,m,r,b[N];
long long sum[N<<2],lazy[N<<2];
int mp[N];
void dfs(int u, int fa)
{
st[u]=++tmp;
mp[tmp] = u;
for(auto v:q[u])
{
if(v==fa) continue;
dfs(v,u);
}
en[u]=tmp;
}
int a[1005],c[1005];
void pushup(int i)
{
sum[i]=sum[i<<1]+sum[i<<1|1];
}
void up(int i,long long len,long long v)
{
sum[i]+=len * v;
lazy[i]+=v;
}
void pushdown(int i,int l,int r)
{
int mid=(l+r)/2;
if(lazy[i])
{
up(i<<1,mid-l+1,lazy[i]);
up(i<<1|1,r-mid,lazy[i]);
lazy[i] = 0;
}
}
void build(int i,int l,int r)
{
if(l==r) {
sum[i] = b[mp[l]];
return ;
}
int mid=(l+r)/2;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
pushup(i);
}
//區間修改
void zeng(int i,int ql,int qr,int l,int r,int v)
{
if(ql<=l && qr >=r)
{
up(i,r-l+1,v);
return ;
}
int mid=(l+r)/2;
pushdown(i,l,r);
if(ql<=mid)
zeng(i<<1,ql,qr,l,mid,v);
if(qr>mid)
zeng(i<<1|1,ql,qr,mid+1,r,v);
pushup(i);
}
//區間查詢
long long he(int i,int ql,int qr,int l,int r)
{
if(ql<=l && qr >=r)
{
return sum[i];
}
int mid=(l+r)/2;
pushdown(i,l,r);
long long ans = 0;
if(ql<=mid)
ans+=he(i<<1,ql,qr,l,mid);
if(qr>mid)
ans+=he(i<<1|1,ql,qr,mid+1,r);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m>>r;
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
int x,y;
for(int i=1;i<n;i++)
{
cin>>x>>y;
q[x].push_back(y);
q[y].push_back(x);
}
dfs(r,0);
// dfs序
build(1, 1, n);
int f;
while(m--)
{
cin>>f;
if(f==1)
{
cin>>x>>y;
// st[x], en[x]可找到子樹的區間
zeng(1, st[x], en[x], 1, n, y);
}
else
{
cin>>x;
cout<<he(1,st[x],en[x],1,n)<<endl;
}
}
return 0;
}