ABC294G Distance Queries on a Tree
唯一会的 G。
给 \(n\) 个点的树,\(Q\) 次操作:
\(\texttt{1 }i\texttt{ }w\),将第 \(i\) 条边边权改为 \(w\)。
\(\texttt{2 }x\texttt{ }y\),查询 \(x,y\) 之间路径的权值和。
\(n,Q\le 2\times 10^5\)。
树剖板子,运用边转点技巧,由于只有单点修改,套上树状数组维护即可。
简单来说,我们先随便从一个点开始 DFS,将边权转化为其深度较大的节点的权值,然后就变成了对点的操作了。
时间复杂度为 \(\mathcal{O}(Q\log^2 n)\),空间复杂度为 \(\mathcal{O}(n)\)。
$\bf\color{orange}点击查看代码$
#include<bits/stdc++.h>
#define N 200005
#define int long long
using namespace std;
int n,q,f[N],h[N],t[N],sz[N],dfn[N],to[N],d[N],bit[N],val[N];
struct edge{
int v,w,id;
};
vector<edge>g[N];
void modify(int x,int k){
for(int i=x;i<=n;i+=i&(-i)){
bit[i]+=k;
}
}
int query(int x){
int ret=0;
for(int i=x;i;i-=i&(-i)){
ret+=bit[i];
}
return ret;
}
void dfs1(int u,int fa){
sz[u]=1;
for(auto[v,w,id]:g[u]){
if(v^fa){
f[v]=u;
to[id]=v;
val[v]=w;
d[v]=d[u]+1;
dfs1(v,u);
sz[u]+=sz[v];
}
}
}
void dfs2(int u,int fa){
for(auto[v,w,id]:g[u]){
if(v^fa){
if((sz[v]<<1)>sz[u]){
t[v]=t[u];
h[u]=v;
}else{
t[v]=v;
}
dfs2(v,u);
}
}
}
void dfs3(int u,int fa){
++dfn[0];
dfn[u]=dfn[0];
modify(dfn[u],val[u]);
if(h[u]){
dfs3(h[u],u);
}
for(auto[v,w,id]:g[u]){
if((v^fa)&&(v^h[u])){
dfs3(v,u);
}
}
}
int pathquery(int x,int y){
int ret=0;
while(t[x]^t[y]){
if(d[t[x]]<d[t[y]]){
swap(x,y);
}
ret+=query(dfn[x])-query(dfn[t[x]]-1);
x=f[t[x]];
}
if(x^y){
if(d[x]>d[y]){
swap(x,y);
}
ret+=query(dfn[y])-query(dfn[x]);
}
return ret;
}
signed main(){
scanf("%lld",&n);
for(int i=1,u,v,w;i^n;++i){
scanf("%lld%lld%lld",&u,&v,&w);
g[u].push_back({v,w,i});
g[v].push_back({u,w,i});
}
dfs1(1,0);
t[1]=1;
dfs2(1,0);
dfs3(1,0);
scanf("%lld",&q);
for(int op,x,y;q--;){
scanf("%lld%lld%lld",&op,&x,&y);
if(op==1){
modify(dfn[to[x]],-val[to[x]]);//修改相当于减去原值再加上新值。
modify(dfn[to[x]],y);
val[to[x]]=y;
}else{
printf("%lld\n",pathquery(x,y));
}
}
}

浙公网安备 33010602011771号