题解 三只企鹅
题目描述
数据范围:
n
,
m
,
w
≤
1
0
5
,
u
,
v
≤
n
n,m,w \leq 10^5,u,v \leq n
n,m,w≤105,u,v≤n
具体做法与心路历程
考场上的错误方法不多说。
具体做法
我们把查询操作写出来:
a
n
s
=
∑
v
∈
M
o
d
i
f
y
d
i
s
u
+
d
i
s
v
−
2
d
i
s
l
c
a
u
,
v
ans=\sum_{v \in Modify}{dis_u+dis_v-2dis_{lca_{u,v}}}
ans=v∈Modify∑disu+disv−2dislcau,v
我们发现前面两个
d
i
s
dis
dis比较好求。重点是怎么求
d
i
s
l
c
a
u
,
v
dis_{lca_{u,v}}
dislcau,v。
因为直接修改其他所有点的距离没有优秀的方法,所以我们试着把修改改成差分。
对于 u , v u,v u,v,如果他们在 r o o t root root的不同子树中,那么他们的 d i s l c a u , v = 0 dis_{lca_{u,v}}=0 dislcau,v=0,不用考虑。
也就是说,对于一个修改操作 v v v,他能影响到的点只在他所在的到根的子树中。
我们把 v v v到根的路径上的每个点都打一个标记,记 u u u的标记个数为 c n t u cnt_u cntu,考虑查询时求出 u u u的每个祖先作为 l c a lca lca的个数再来计算。
那么有如下计算方式:
- d i s f u × ( c n t f u − c n t u ) dis_{f_u} \times (cnt_{f_u}-cnt_u) disfu×(cntfu−cntu)。
- u = f u u=f_u u=fu
- 重复1,2,直到 u u u为根
将以上拆开后把 c n t u cnt_u cntu放在一起有: c n t u × ( d i s u − d i s f u ) cnt_u \times (dis_u - dis_{f_u}) cntu×(disu−disfu)。那么这个查询就变成查询 u u u到根上这些所有的和了。
我们可以轻易维护好 c n t u cnt_u cntu(树链剖分),那么我们用线段树维护每个点 u u u的 d i s u − d i s f u dis_{u}-dis_{f_u} disu−disfu的倍数,最后区间求和即可。(意思就是区间加时对于每个点加的值不一样)
如果不清楚可以看代码。
C o d e \mathcal{Code} Code
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月28日 星期一 20时55分56秒
*******************************/
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
struct edge{
int to,next,w;
edge(int a=0,int b=0,int c=0):to(a),next(b),w(c){}
};
const int maxn=2e5+10;
int n,m,head[maxn],cnt,f[maxn],top[maxn],size[maxn],seg[maxn],son[maxn],dis[maxn],tot,rev[maxn];
long long res;
edge e[maxn<<1];
void add(int u,int v,int w)
{
e[++cnt]=edge(v,head[u],w);
head[u]=cnt;
}
void dfs1(int now,int fa)
{
f[now]=fa; size[now]=1;
for(int i=head[now];i;i=e[i].next)
if(e[i].to!=fa)
{
dis[e[i].to]=dis[now]+e[i].w;
dfs1(e[i].to,now);
size[now]+=size[e[i].to];
if(size[son[now]]<size[e[i].to])
son[now]=e[i].to;
}
}
void dfs2(int now,int tp)
{
seg[now]=++seg[0];
rev[seg[0]]=now;
top[now]=tp;
if(son[now])
dfs2(son[now],tp);
for(int i=head[now];i;i=e[i].next)
if(e[i].to!=f[now] && e[i].to!=son[now])
dfs2(e[i].to,e[i].to);
}
/*{{{线段树*/
namespace SegmentTree{
long long sum[maxn*4],lazy[maxn*4],delta[maxn*4];
void update(int k)
{
sum[k]=sum[k<<1]+sum[k<<1|1];
}
void work(int k,long long val)
{
sum[k]+=delta[k]*val;
lazy[k]+=val;
}
void pushdown(int k)
{
if(!lazy[k]) return;
work(k<<1,lazy[k]);
work(k<<1|1,lazy[k]);
lazy[k]=0;
}
void build(int k,int l,int r)
{
if(l==r)
{
delta[k]=dis[rev[l]]-dis[f[rev[l]]];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
delta[k]=delta[k<<1]+delta[k<<1|1];
}
void modify(int k,int l,int r,int x,int y,int val)
{
if(l>=x && r<=y) return work(k,val);
if(l>y || r<x) return;
int mid=(l+r)>>1;
pushdown(k);
modify(k<<1,l,mid,x,y,val); modify(k<<1|1,mid+1,r,x,y,val);
return update(k);
}
long long query(int k,int l,int r,int x,int y)
{
if(l>=x && r<=y) return sum[k];
if(l>y || r<x) return 0;
int mid=(l+r)>>1;
pushdown(k);
return query(k<<1,l,mid,x,y)+query(k<<1|1,mid+1,r,x,y);
}
};
/*}}}*/
void modify(int now)
{
++tot; res+=dis[now];
while(top[now])
{
SegmentTree::modify(1,1,seg[0],seg[top[now]],seg[now],1);
now=f[top[now]];
}
}
long long solve(int now)
{
long long res=::res+tot*dis[now];
while(top[now])
{
res-=2LL * SegmentTree::query(1,1,seg[0],seg[top[now]],seg[now]);
now=f[top[now]];
}
return res;
}
signed main()
{
/*freopen("express.in","r",stdin);*/
/*freopen("express.out","w",stdout);*/
cin>>n>>m;
int u,v,w;
for(int i=1;i<n;i++)
{
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dfs1(1,0); dfs2(1,1);
SegmentTree::build(1,1,seg[0]);
while(m--)
{
int opt,u;
cin>>opt>>u;
if(opt==1)
modify(u);
else
printf("%lld\n",solve(u));
}
return 0;
}