#树链剖分,并查集,树状数组#洛谷 4949 最短距离
题目大意
给出一个基环树,支持修改边权和查询两点间最短距离
分析
如果是一棵树那就很简单了,树链剖分,将边权变成子节点的点权,然后因为只有单边修改,所以可以用常数比较小的树状数组;
但是这是一棵基环树,考虑将环上的一条边拎出来,对其它边用树的方法做\(x->y\),这条边特判,也就是\(x->zx->zy->y\)或者\(x->zy->zx->y\),一共三种情况求最小值,注意查询时的区间边界
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011; struct node{int y,w,next;}e[N<<1];
int c[N],a[N],dep[N],dfn[N],top[N],f[N];
int fat[N],big[N],son[N],ls[N],tot,n,m,K=1,zx,zy,zw,zp;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void add(int x,int y){for (;x<=n;x+=-x&x) c[x]+=y;}
inline signed query(int x){rr int ans=0; for (;x;x-=-x&x) ans+=c[x]; return ans;}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline signed Query(int x,int y){
rr int ans=0;
while (top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]) x^=y,y^=x,x^=y;
ans+=query(dfn[x])-query(dfn[top[x]]-1);
x=fat[top[x]];
}
if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
return ans+=query(dfn[y])-query(dfn[x]);//[dfn[x]+1~dfn[y]]最上面的点权不算
}
inline void dfs1(int x,int fa,int W){
dep[x]=dep[fa]+1,fat[x]=fa,son[x]=1,c[x]=W;
for (rr int i=ls[x],mson=-1;i;i=e[i].next)
if (e[i].y!=fa){
dfs1(e[i].y,x,e[i].w);
son[x]+=son[e[i].y];
if (son[e[i].y]>mson) big[x]=e[i].y,mson=son[e[i].y];
}
}
inline void dfs2(int x,int linp){
dfn[x]=++tot,a[tot]=c[x],top[x]=linp;
if (!big[x]) return; dfs2(big[x],linp);
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=fat[x]&&e[i].y!=big[x]) dfs2(e[i].y,e[i].y);
}
signed main(){
n=iut(); m=iut();
for (rr int i=1;i<=n;++i) f[i]=i;
for (rr int i=1;i<=n;++i){
rr int x=iut(),y=iut(),w=iut();
rr int fa=getf(x),fb=getf(y);
if (fa==fb) {zx=x,zy=y,zw=w,zp=i,K+=2; continue;}
if (fa>fb) fa^=fb,fb^=fa,fa^=fb; f[fa]=fb;
e[++K]=(node){y,w,ls[x]},ls[x]=K;
e[++K]=(node){x,w,ls[y]},ls[y]=K;
}
dfs1(1,0,0),dfs2(1,1);
for (rr int i=1;i<=n;++i) c[i]=c[i-1]+a[i];
for (rr int i=n;i>=1;--i) c[i]-=c[i&(i-1)];//O(n)预处理树状数组
for (rr int i=1;i<=m;++i){
rr int op=iut(),x=iut(),y=iut();
if (op==1){
if (x==zp) zw=y;
else{
rr int xx=e[x<<1].y,yy=e[x<<1|1].y;
if (dep[xx]<dep[yy]) xx^=yy,yy^=xx,xx^=yy;
add(dfn[xx],y-a[dfn[xx]]),a[dfn[xx]]=y;
}
}else{
rr int t1=Query(x,zx)+Query(y,zy);
rr int t2=Query(x,zy)+Query(y,zx);
rr int ans1=Query(x,y),ans2=(t1<t2?t1:t2)+zw;
print(ans1<ans2?ans1:ans2),putchar(10);
}
}
return 0;
}