#树链剖分,并查集,树状数组#洛谷 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;
}
posted @ 2020-02-19 13:54  lemondinosaur  阅读(146)  评论(0编辑  收藏  举报