HDU 6393 Traffic Network in Numazu (樹剖+線段樹)

題目鏈接

題意:

給定一個 nn 個點 nn 條邊的圖,每條邊都權值,有 qq 次操作,一種操作修改一條邊的值,另一種操作查詢 xxyy 的最短路
(1<=n,q<=1e5)(1<=n,q<=1e5)

思路:

如果是一顆樹那麼這個問題可以被樹剖加線段樹輕鬆的解決,已知這張圖實際上是一棵樹加上一條邊構成,那麼可以將其刪掉一條邊 (u,v)(u,v) 變成一顆樹進行處理。那麼現在查詢 xyx \to y 的最短路,可以分成三種情況,一種直接從樹上走 xlca(x,y)yx \to lca(x,y) \to y,一種經過 uvu \to vxuvyx \to u \to v \to y,另一種反過來 xvuyx \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)));
			}
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章