题意
给出一棵 (n) 个点的树,每个点 (x) 有一个 (f(x)) 值,初始全为 (0)。现在有三种操作共 (m) 次:
- 输入:(x,w),选择一个点 (x) ,对于树上的所有点 (y),(f(y)) 将增加 (w-dist(x,y))。其中,(dist(x,y)) 表示 (x) 与 (y) 之间最短路径的边数。
- 输入:(x),更新 (f(x)) 为 (min(f(x),0))。
- 输入: (x) ,询问 (f(x))。
(1leq n,m leq 5 imes 10^4,0leq w leq 10000)
题目链接:https://ac.nowcoder.com/acm/contest/5672/C
分析
难点在于操作 (1) 如何实现,每次修改所有点必然不现实,因此,要想办法将影响保存起来,在查询的时候直接处理。
首先,对于 (w-dist(x,y)),可以写成 ((w-depth[x])-(depth[y])+(2 imes dpeth[lca(x,y)]))。对于其中的 (3) 部分,分别处理。((w-depth[x])) 可以在操作 (1) 的时候,一边操作,一边记录。对于 ((depth[y])) ,只要知道在查询当前点之前进行了多少次操作 (1) ,就可以直接算出。对于 ((2 imes depth[lca(x,y)])),只要在每次操作 (1) 时,将点 (x) 到根节点的路径上所有边的值加 (2) ,在查询的时候,只要从查询点向根节点求出路径上的边的权值和即可。
对于 (2),将上一次修改为 (0) 的结果记录下来,下次查询的时候减去即可。
代码
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=5e4+5;
vector<int>pic[N];
int fa[N],depth[N],num[N];
int top[N],dfn[N],sz[N],son[N];
int tree[N<<2],n,lazy[N<<2];
void dfs1(int u,int p,int d)
{
depth[u]=d;
fa[u]=p;
sz[u]=1;
son[u]=0;
for(int i=0;i<pic[u].size();i++)
{
int v=pic[u][i];
if(v==p) continue;
dfs1(v,u,d+1);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])
son[u]=v;
}
}
void dfs2(int u,int p,int tp,int &cnt)
{
top[u]=tp;
dfn[u]=++cnt;
if(son[u]==0) return;
dfs2(son[u],u,tp,cnt);
for(int i=0;i<pic[u].size();i++)
{
int v=pic[u][i];
if(v==p||v==son[u]) continue;
dfs2(v,u,v,cnt);
}
}
void pushup(int rt)
{
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void pushdown(int ln,int rn,int rt)
{
if(lazy[rt])
{
tree[rt<<1]+=ln*lazy[rt];
tree[rt<<1|1]+=rn*lazy[rt];
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
lazy[rt]=0;
}
}
void build(int l,int r,int rt)
{
lazy[rt]=0;
if(l==r)
{
tree[rt]=0;
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void update(int l,int r,int L,int R,int val,int rt)
{
if(L<=l&&r<=R)
{
tree[rt]+=val*(r-l+1);
lazy[rt]+=val;
return;
}
int mid=(l+r)>>1;
pushdown(mid-l+1,r-mid,rt);
if(L<=mid) update(l,mid,L,R,val,rt<<1);
if(R>mid) update(mid+1,r,L,R,val,rt<<1|1);
pushup(rt);
}
int query(int l,int r,int L,int R,int rt)
{
if(L<=l&&r<=R)
return tree[rt];
int mid=(l+r)>>1,res=0;
pushdown(mid-l+1,r-mid,rt);
if(L<=mid) res+=query(l,mid,L,R,rt<<1);
if(R>mid) res+=query(mid+1,r,L,R,rt<<1|1);
return res;
}
void change(int u,int v,int val)
{
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]])
swap(u,v);
update(1,n,dfn[top[u]],dfn[u],val,1);
u=fa[top[u]];
}
if(depth[u]<depth[v]) swap(u,v);
update(1,n,dfn[v],dfn[u],val,1);
}
int ask(int u,int v)
{
int res=0;
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]]) swap(u,v);
res+=query(1,n,dfn[top[u]],dfn[u],1);
u=fa[top[u]];
}
if(depth[u]<depth[v]) swap(u,v);
res+=query(1,n,dfn[v],dfn[u],1);
return res;
}
int main()
{
int T,m;
scanf("%d",&T);
while(T--)
{
int x,y,cnt=0,sum=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
pic[i].clear();
num[i]=0;
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
pic[x].pb(y);
pic[y].pb(x);
}
dfs1(1,0,0);
dfs2(1,0,1,cnt);
build(1,cnt,1);
int opt,w,cot=0;
while(m--)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d",&x,&w);
change(1,x,2);
change(1,1,-2);
cot++;
sum+=(w-depth[x]);
}
else if(opt==2)
{
scanf("%d",&x);
int tmp=sum-depth[x]*cot+ask(1,x)-ask(1,1)-num[x];
if(tmp>0) num[x]+=tmp;//记录
}
else
{
scanf("%d",&x);
int tmp=sum-depth[x]*cot+ask(1,x)-ask(1,1)-num[x];
printf("%d
",tmp);
}
}
}
return 0;
}