HDU 6393 Traffic Network in Numazu (树剖+线段树)

题目链接

题意:

给定一个 n n 个点 n n 条边的图,每条边都权值,有 q q 次操作,一种操作修改一条边的值,另一种操作查询 x x y y 的最短路
( 1 < = n , q < = 1 e 5 ) (1<=n,q<=1e5)

思路:

如果是一颗树那么这个问题可以被树剖加线段树轻松的解决,已知这张图实际上是一棵树加上一条边构成,那么可以将其删掉一条边 ( u , v ) (u,v) 变成一颗树进行处理。那么现在查询 x y x \to y 的最短路,可以分成三种情况,一种直接从树上走 x l c a ( x , y ) y x \to lca(x,y) \to y ,一种经过 u v u \to v x u v y x \to u \to v \to y ,另一种反过来 x v u y x \to v \to u \to y ,取一个最小值即可。

代码:

#include <bits/stdc++.h>
#define ll long long
#define ls x<<1
#define rs x<<1|1
using namespace std;
const int N=3e5+10;
int TT,n,q,op,x,y,U,V,W;
int bq[N],dq[N],dy[N];//边权和点权,将边权放到深度更大的点上,保存对应关系

//树链剖分
struct edge{
	int to,next,id,w;
}e[N*2];
int cnt,head[N];
void add(int u,int v,int w,int id){
	e[++cnt].to=v;e[cnt].next=head[u];e[cnt].id=id;e[cnt].w=w;head[u]=cnt++;
}
int tot[N],son[N],deep[N],fa[N];
int dfs1(int u,int f,int dep){
	tot[u]=1;fa[u]=f;deep[u]=dep;
	int pd=-1;
	for(int i=head[u];~i;i=e[i].next){
		int v=e[i].to;
		if(v==f)continue;
		dq[v]=bq[e[i].id];dy[e[i].id]=v;
		tot[u]+=dfs1(v,u,dep+1);
		if(tot[v]>pd)pd=tot[v],son[u]=v;
	}
	return tot[u];
}
int idx[N],top[N],id[N],cnt1;
void dfs2(int u,int f){
	idx[u]=++cnt1,id[cnt1]=u;
	top[u]=f;
	if(!son[u])return ;
	dfs2(son[u],f);
	for(int i=head[u];~i;i=e[i].next){
		int v=e[i].to;
		if(!idx[v])dfs2(v,v);
	}
}
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	return x;
}
//线段树
struct node{
	int l,r;
	ll sum;
}T[N*4];
void up(int x){
	T[x].sum=T[ls].sum+T[rs].sum;
}
void built(int x,int l,int r){
	T[x].l=l;T[x].r=r;
	if(l==r){
		T[x].sum=dq[id[l]];return ;
	}
	int mid=(l+r)/2;
	built(ls,l,mid);built(rs,mid+1,r);
	up(x);
}
void add(int x,int pos,ll val){
	if(T[x].l==T[x].r){
		T[x].sum=val;return ;
	}
	int mid=(T[x].l+T[x].r)/2;
	if(pos<=mid)add(ls,pos,val);
	else add(rs,pos,val);
	up(x);
}
ll query(int x,int LL,int RR){
	if(T[x].l>=LL&&T[x].r<=RR){
		return T[x].sum;
	}
	int mid=(T[x].l+T[x].r)/2;
	ll ans=0;
	if(LL<=mid)ans+=query(ls,LL,RR);
	if(RR>mid)ans+=query(rs,LL,RR);
	return ans;
}
ll ljsum(int x,int y){
	ll ans=0;
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		ans+=query(1,idx[top[x]],idx[x]);
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	ans+=query(1,idx[x],idx[y]);
	//cout<<x<<endl;
	ans-=dq[x];//lca的边权被计算,应该减去
	return ans;
}
void init(int n){
	memset(head,-1,sizeof(head));cnt=cnt1=0;
	for(int i=1;i<=n;i++)idx[i]=id[i]=top[i]=fa[i]=deep[i]=son[i]=tot[i]=dy[i]=dq[i]=0;
}
int main()
{
	scanf("%d",&TT);
	while(TT--){
		scanf("%d%d",&n,&q);init(n);
		for(int i=1,u,v,w;i<n;i++){
			scanf("%d%d%d",&u,&v,&w);
			add(u,v,w,i);add(v,u,w,i);bq[i]=w;
		}
		dfs1(1,0,1);dfs2(1,0);built(1,1,n);
		scanf("%d%d%d",&U,&V,&W);//删去一条边
		while(q--){
			scanf("%d%d%d",&op,&x,&y);
			if(op==0){
				if(x!=n)add(1,idx[dy[x]],y),dq[dy[x]]=y;
				else W=y;
			}else{
				ll ans1=ljsum(x,y);//3种情况讨论
				ll ans2=ljsum(x,U)+W+ljsum(V,y);
				ll ans3=ljsum(x,V)+W+ljsum(U,y);
				printf("%lld\n",min(ans1,min(ans2,ans3)));
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40400202/article/details/101464055