题目描述
输入描述:
输出描述:
示例1
输入
1
5 6
1 2
1 3
2 4
2 5
1 1 5
3 4
2 1
1 2 7
3 3
3 1
输出
3
9
6
题目大意
有一个国家,它的城市分布可以表现为一棵树。其中爆发了疫情,我们定义
f
(
n
)
f(n)
f(n)表示
n
n
n号城市的疫情严重性。有以下3种操作:
1、蔓延,给定一个
x
,
w
x,w
x,w。对于所有城市
y
y
y,其严重程度会加上
w
−
d
i
s
t
(
x
,
y
)
w-dist(x,y)
w−dist(x,y),其中
d
i
s
t
(
x
,
y
)
dist(x,y)
dist(x,y)表示两个城市之间的距离。可以为负数。
2、消除,给定一个
y
y
y,将
y
y
y城市的
f
(
y
)
f(y)
f(y)更改为
m
i
n
(
0
,
f
(
y
)
)
min(0,f(y))
min(0,f(y))。
3、询问,给定一个
x
x
x,输出
f
(
x
)
f(x)
f(x)。
如果出现负数,可以忽略。现对于每个询问,要求输出答案。
分析
分析算法,显然,在一棵树上进行加减询问的,就是树剖。
那么我们对于每个操作进行分析。
-
o
p
1
op1
op1
发现有 d i s t dist dist这个函数,我们想在树上求距离,是用 l c a lca lca做的。不妨我们将每个城市的变换展开:
设 x , y , l c a x,y,lca x,y,lca的深度为 d e p [ x ] , d e p [ y ] , d e p [ l c a ] dep[x],dep[y],dep[lca] dep[x],dep[y],dep[lca]。
f ( y ) + = w − d i s t ( x , y ) f(y)+=w-dist(x,y) f(y)+=w−dist(x,y)
= w − ( d e p [ x ] + d e p [ y ] − 2 d e p [ l c a ] ) \qquad\,\,\,\,\,\,=w-(dep[x]+dep[y]-2dep[lca]) =w−(dep[x]+dep[y]−2dep[lca])
= w − d e p [ x ] − d e p [ y ] + 2 d e p [ l c a ] \qquad\,\,\,\,\,\,=w-dep[x]-dep[y]+2dep[lca] =w−dep[x]−dep[y]+2dep[lca]
我们可以发现,对于一个 o p 1 op1 op1而言, w − d e p [ x ] w-dep[x] w−dep[x]是固定的,而对于每个城市 y y y而言, d e p [ y ] dep[y] dep[y]也是固定的。因此不妨我们开一个变量存储:
A + = w − d e p [ x ] , B + + ; A+=w-dep[x],\qquad B++; A+=w−dep[x],B++;
A A A 存所有关于 x x x的效果, B B B存 d e p [ y ] dep[y] dep[y]的次数。
这样对于每次询问或者消除,我都可以通过查询之前存下的所有蔓延的累加效果。所以,如果要用 f ( y ) f(y) f(y)时,我们可以这样算:
f ( y ) = A − B ∗ d e p [ y ] + 2 ∑ ( d e p [ l c a ] ) f(y)=A-B*dep[y]+2\sum(dep[lca]) f(y)=A−B∗dep[y]+2∑(dep[lca]) -
o
p
2
op2
op2
对于这个操作,由于我们要考虑是正还是负,所以对于 m i n ( 0 , f ( y ) ) min(0,f(y)) min(0,f(y))我们也存储一下。
不妨搞一个 C C C数组,存入每次 y y y的消除结果。因此有:
C y + = m i n ( 0 , f ( y ) ) − f ( y ) C_y+=min(0,f(y))-f(y) Cy+=min(0,f(y))−f(y)
之后无论是算答案,还是继续,只要在上面那串后面加上 C y C_y Cy即可。即:
f ( y ) = A − B ∗ d e p [ y ] + 2 ∑ ( d e p [ l c a ] ) + C y f(y)=A-B*dep[y]+2\sum(dep[lca])+C_y f(y)=A−B∗dep[y]+2∑(dep[lca])+Cy -
o
p
3
op3
op3
这个经过上面的简化之后,它就简单了。如上,我已经写出了求解的公式。
那现在有个问题吼,怎么搞
l
c
a
lca
lca了?不妨画个图试试。
图中红色点为
x
x
x,其他节点中的数字为和
x
x
x的
l
c
a
lca
lca的深度。
绿色路径是红色节点到根的路径,那么可以发现,每个节点与
x
x
x的
l
c
a
lca
lca的深度是这个节点到根的路径上的绿色边的数量。那么我们只要搞下树剖,然后线段树维护一下就可以了,见代码。
代码
#include<bits/stdc++.h>
#define debug cerr<<"debug";
#define ll long long
#define ls i<<1
#define rs i<<1|1
using namespace std;
const int MAXN=1e5+10;
int siz[MAXN],ldfn[MAXN],rdfn[MAXN],dep[MAXN],tot,A,B;
int son[MAXN],fa[MAXN],id[MAXN],top[MAXN],val[MAXN],C[MAXN];
vector<int> vec[MAXN];
void predfs(int pos)
{
siz[pos]=1;son[pos]=0;
for(int i=0;i<vec[pos].size();i++){
int s=vec[pos][i];
if(s==fa[pos]) continue;
fa[s]=pos;dep[s]=dep[pos]+1;
predfs(s);siz[pos]+=siz[s];
if(siz[s]>siz[son[pos]]) son[pos]=s;
}
}//树剖板子
void dfs(int pos,int tp)
{
top[pos]=tp;ldfn[pos]=++tot;id[tot]=pos;
if(son[pos]) dfs(son[pos],tp);
for(int i=0;i<vec[pos].size();i++){
int s=vec[pos][i];
if(s==son[pos]||s==fa[pos]) continue;
dfs(s,s);
}rdfn[pos]=tot;
}//树剖板子++
struct tree{
int l,r;
ll inc,sum;
}tr[MAXN<<2];
void build(int i,int l,int r)
{
tr[i].l=l;
tr[i].r=r;
tr[i].inc=0;
if(l==r){
tr[i].sum=0;
return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tr[i].sum=tr[i<<1].sum+tr[i<<1|1].sum;
}
void add(int i,int a,int b,int c)
{
if(tr[i].l==a&&tr[i].r==b){
tr[i].inc+=c;
return;
}
tr[i].sum+=(b-a+1)*c;
int mid=(tr[i].l+tr[i].r)>>1;
if(b<=mid) add(i<<1,a,b,c);
else if(a>mid) add(i<<1|1,a,b,c);
else{
add(i<<1,a,mid,c);
add(i<<1|1,mid+1,b,c);
}
}
ll query(int i,int a,int b)
{
if(tr[i].l==a&&tr[i].r==b)
return tr[i].sum+tr[i].inc*(b-a+1);
if(tr[i].inc){
tr[i].sum+=tr[i].inc*(tr[i].r-tr[i].l+1);
tr[i<<1].inc+=tr[i].inc;
tr[i<<1|1].inc+=tr[i].inc;
tr[i].inc=0;
}
int mid=(tr[i].l+tr[i].r)>>1;
if(b<=mid) return query(i<<1,a,b);
else if(a>mid) return query(i<<1|1,a,b);
else return query(i<<1,a,mid)+query(i<<1|1,mid+1,b);
}//线段树区间加减板子
void CP(int x,int y,ll val)//CP change path
{
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]) swap(x,y);
add(1,ldfn[top[y]],ldfn[y],val);y=fa[top[y]];
}if(dep[x]>dep[y]) swap(x,y);
if(x==y) return;//这里是边转点,因此lca不能算
add(1,ldfn[x]+1,ldfn[y],val);
}//更改绿色边
int GPS(int x,int y)//GPS get path sum
{
ll ret=0;
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]) swap(x,y);
ret+=query(1,ldfn[top[y]],ldfn[y]);y=fa[top[y]];
}if(dep[x]>dep[y]) swap(x,y);
if(x==y) return ret;//同
ret+=query(1,ldfn[x]+1,ldfn[y]);
return ret;
}//查询路径上的绿色边
int main()
{
int t,n,m,p,x,y,k,op;scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) vec[i].clear();
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
vec[x].push_back(y);
vec[y].push_back(x);
}tot=0;dep[1]=0;
predfs(1);dfs(1,1);
build(1,1,tot);A=B=0;
memset(C,0,sizeof(C));
while(m--){
scanf("%d",&op);
if(op==1){
scanf("%d%d",&x,&k);
A+=k-dep[x];B++;
CP(1,x,2ll);//这里将上面的式子中的*2放到每个式子里做了
}
if(op==2){
scanf("%d",&x);
ll ans=0;
ans+=GPS(1,x);
ans=A-B*dep[x]+ans+C[x];
C[x]+=min(0ll,ans)-ans;
}
if(op==3){
scanf("%d",&x);
ll ans=0;ans+=GPS(1,x);
ans=A-B*dep[x]+ans+C[x];
printf("%lld\n",ans);
}
}
}
}
END
不会吧不会吧,不会还有人没有习惯阴间函数名吧,不会吧不会吧……