P3833 [SHOI2012]魔法樹
阿新 • • 發佈:2020-11-18
題目背景
SHOI2012 D2T3
題目描述
Harry Potter 新學了一種魔法:可以讓改變樹上的果子個數。滿心歡喜的他找到了一個巨大的果樹,來試驗他的新法術。
這棵果樹共有 N個節點,其中節點 0 是根節點,每個節點 u的父親記為 fa[u],保證有 fa[u] < u 。初始時,這棵果樹上的果子都被 Dumbledore 用魔法清除掉了,所以這個果樹的每個節點上都沒有果子(即 0 個果子)。
不幸的是,Harry 的法術學得不到位,只能對樹上一段路徑的節點上的果子個數統一增加一定的數量。也就是說,Harry 的魔法可以這樣描述:A u v d
。表示將點 u和 v 之間的路徑上的所有節點的果子個數都加上 d**。
接下來,為了方便檢驗 Harry 的魔法是否成功,你需要告訴他在釋放魔法的過程中的一些有關果樹的資訊:Q u
。表示當前果樹中,以點 uu 為根的子樹中,總共有多少個果子?
輸入格式
第一行一個正整數 \(N (1 \leq N \leq 100000)\),表示果樹的節點總數,節點以 \(0,1,\dots,N - 1\) 標號,0 一定代表根節點。
接下來 N - 1 行,每行兩個整數 \(a,b (0 \leq a < b < N)\),表示 a 是 b 的父親。
接下來是一個正整數 \(Q(1 \leq Q \leq 100000)\),表示共有 \(Q\) 次操作。
後面跟著 \(Q\) 行,每行是以下兩種中的一種:
A u v d
,表示將 \(u\) 到 \(v\) 的路徑上的所有節點的果子數加上 \(d\)。保證 \(0 \leq u,v < N,0 < d < 100000\)Q u
,表示詢問以 \(u\) 為根的子樹中的總果子數,注意是包括 \(u\)本身的。
輸出格式
對於所有的 Q
操作,依次輸出詢問的答案,每行一個。答案可能會超過 \(2^{32}\),但不會超過 \(10^{15}\) 。
輸入輸出樣例
輸入 #1複製
4
0 1
1 2
2 3
4
A 1 3 1
Q 0
Q 1
Q 2
輸出 #1複製
3
3
2
思路
支援兩個操作,鏈上修改,子樹查詢
樹剖裸題,程式碼就在板子基礎上,改改輸入輸出就行
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=200100;
typedef long long LL;
const int M=N*2;
int n,m;
int head[N],ver[N],ne[N],idx;
int w[N];
int id[N],nw[N],cnt;
int dep[N],sz[N],top[N],fa[N],son[N];
char ch[4];
struct node
{
int l,r;
LL add,sum;
} tr[N<<2];
void add(int u,int v)
{
ne[idx]=head[u];
ver[idx]=v;
head[u]=idx;
idx++;
}
void dfs1(int u,int father,int depth)
{
sz[u]=1;
fa[u]=father;
dep[u]=depth;
for(int i=head[u]; i!=-1; i=ne[i])
{
int j=ver[i];
if(j==father)continue;
dfs1(j,u,depth+1);
sz[u]+=sz[j];
if(sz[son[u]]<sz[j]) son[u]=j;//sz大小,
}
}
void dfs2(int u,int t) //t儲存u所在當前重鏈的頂點是誰
{
id[u]=++cnt;//dfs序
nw[cnt]=w[u];
top[u]=t;
if(!son[u]) return;
dfs2(son[u],t);
for(int i=head[u]; i!=-1; i=ne[i])
{
int j=ver[i];
if(j==fa[u]||j==son[u])continue;
dfs2(j,j);
}
}
struct tree
{
inline void pushup(int p)
{
tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
}
inline void pushdown(int p)
{
if(tr[p].add)
{
tr[p<<1].sum+=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].add;
tr[p<<1].add+=tr[p].add;
tr[p<<1|1].sum+=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].add;
tr[p<<1|1].add+=tr[p].add;
tr[p].add=0;
}
}
void build(int p,int l,int r)
{
tr[p]= {l,r,0,nw[l]};
if(l==r) return;
int mid=(tr[p].l+tr[p].r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void update(int p,int l,int r,int k)
{
if(tr[p].l>=l&&tr[p].r<=r)
{
tr[p].sum+=(tr[p].r-tr[p].l+1)*k;
tr[p].add+=k;
return ;
}
pushdown(p);
int mid=tr[p].l+tr[p].r>>1;
if(l<=mid) update(p<<1,l,r,k);
if(r>mid) update(p<<1|1,l,r,k);
pushup(p);
}
LL query(int p,int l,int r)
{
if(tr[p].l>=l&&tr[p].r<=r)
{
return tr[p].sum;
}
pushdown(p);
int mid=tr[p].l+tr[p].r>>1;
LL ans=0;
if(l<=mid) ans+=query(p<<1,l,r);
if(r>mid) ans+=query(p<<1|1,l,r);
return ans;
}
} segment;
struct tre
{
void update_path(int u,int v,int k)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]])
swap(u,v);
segment.update(1,id[top[u]],id[u],k);
u=fa[top[u]];
}
if(dep[u]<dep[v])
swap(u,v);
segment.update(1,id[v],id[u],k);
}
LL query_path(int u,int v)
{
LL res=0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]])
swap(u,v);
res+=segment.query(1,id[top[u]],id[u]);
u=fa[top[u]];
}
if(dep[u]<dep[v])
swap(u,v);
res+=segment.query(1,id[v],id[u]);
return res;
}
void update_tree(int u,int k)
{
segment.update(1,id[u],id[u]+sz[u]-1,k);
}
LL query_tree(int u)
{
segment.query(1,id[u],id[u]+sz[u]-1);
}
} chain;
inline int read()
{
int x=0;
int f=1;
char ch;
ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10,x=x+ch-'0';
ch=getchar();
}
return x*f;
}
int main()
{
scanf("%d",&n);
memset(head,-1,sizeof(head));
for(int i=1; i<=n-1; i++)
{
int a,b;
a=read();
b=read();
a++;
b++;
add(a,b);
add(b,a);
}
dfs1(1,-1,1);
dfs2(1,1) ;
segment.build(1,1,n);
scanf("%d",&m);
for(int i=1; i<=m; i++)
{
int t,u,v,k;
scanf("%s",ch+1);
if(ch[1]=='A')
{
scanf("%d%d%d",&u,&v,&k);
{
chain.update_path(u+1,v+1,k);
}
}
else
{
scanf("%d",&u);
printf("%lld\n",chain.query_tree(u+1));
}
}
return 0;
}