HDU 6393 Traffic Network In Numazu 环套树+树链剖分

题意:n点n条边的无向带权图,m次操作.
操作1:修改x-y的边权
操作2:询问x-y的最短路.
n,m<=1e5.1<=w[i]<=1e5

因为是n条边的联通图,也就是环套树的形式.
并查集找出环上任意一条边(u,v).

现在(x,y)的最短路可以分为:是否经过边(u,v).
不经过边(u,v) 则(x,y)的距离为树上两点的距离.
经过边(u,v) 注意有两种方案: x->u->v->y, x->v->u->y.
修改边权+树上两点间距离,树链剖分维护一下即可.


 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,M=2e5+5;
int T,Q,n,tot,a[N],b[N],c[N];
int head[N],fa[N],par[N],sz[N],dep[N];
int tim,pos[N],bl[N],cost[N],son[N];
int U,V,W;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
struct edge{
	int to,w,nxt;
}e[M];
struct node{
	int l,r;
	ll sum;
}t[N<<2];
void add_edge(int u,int v,int w){
	e[++tot].to=v,e[tot].w=w;
	e[tot].nxt=head[u],head[u]=tot;
}
void init(){
	memset(head,-1,sizeof(head));
	tim=tot=0;
	scanf("%d%d",&n,&Q);
	for(int i=1;i<=n;i++)	fa[i]=i;
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&a[i],&b[i],&c[i]);
		int x=find(a[i]),y=find(b[i]);
		if(x!=y){
			add_edge(a[i],b[i],c[i]);
			add_edge(b[i],a[i],c[i]);
			fa[x]=y;
		}
		else 	U=a[i],V=b[i],W=c[i];
	}	
}
void dfs(int u){
	sz[u]=1;
	for(int i=head[u];i!=-1;i=e[i].nxt){
		int v=e[i].to;
		if(v==par[u])	continue;
		dep[v]=dep[u]+1,par[v]=u;
		cost[v]=e[i].w;
		dfs(v);
		sz[u]+=sz[v];
	}
}
void dfs(int x,int chain){
	int k=0;
	pos[x]=++tim;
	bl[x]=chain;
	for(int i=head[x];i!=-1;i=e[i].nxt){
		int v=e[i].to;
		if(dep[v]>dep[x]&&sz[v]>sz[k])	k=v;
	}
	if(k==0){
		son[x]=x;
		return;
	}
	son[x]=k;
	dfs(k,chain);
	for(int i=head[x];i!=-1;i=e[i].nxt)
		if(dep[e[i].to]>dep[x]&&k!=e[i].to)
			dfs(e[i].to,e[i].to);
}
void build(int o,int l,int r){
	t[o].l=l,t[o].r=r;
	if(l==r)	return;
	int mid=l+r>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);	
}
void push_up(int o){
	t[o].sum=t[o<<1].sum+t[o<<1|1].sum;
}
void change(int o,int pos,int val){
	int l=t[o].l,r=t[o].r,mid=l+r>>1;
	if(l==r){
		t[o].sum=val;
		return;
	}
	if(pos<=mid)	change(o<<1,pos,val);
	else	change(o<<1|1,pos,val);
	push_up(o);
}
ll querysum(int o,int ql,int qr){
	int l=t[o].l,r=t[o].r,mid=l+r>>1;
	if(l>=ql&&r<=qr)	return t[o].sum;
	ll sum=0;
	if(ql<=mid)	sum+=querysum(o<<1,ql,qr);
	if(qr>mid)	sum+=querysum(o<<1|1,ql,qr);
	return sum;
}
ll solvesum(int x,int y){
	ll sum=0;
	while(bl[x]!=bl[y]){
		if(dep[bl[x]]<dep[bl[y]])	swap(x,y);
		sum+=querysum(1,pos[bl[x]],pos[x]);
		x=par[bl[x]];
	}
	if(pos[x]>pos[y])	swap(x,y);
	sum+=querysum(1,pos[son[x]],pos[y]);
	return sum;
}
void solve(){
	build(1,1,n);
	cost[1]=0;
	for(int i=1;i<=n;i++)	change(1,pos[i],cost[i]);
	int op,x,y,w;
	while(Q--){
		scanf("%d%d",&op,&x);
		if(op==0){
			scanf("%d",&w);
			int u=a[x],v=b[x];
			int son=par[v]==u?v:u;
			cost[son]=w;
			change(1,pos[son],cost[son]);
		}
		else{
			scanf("%d",&y);
			ll v1=solvesum(x,y);
			ll v2=solvesum(x,U)+W+solvesum(V,y);
			ll v3=solvesum(x,V)+W+solvesum(U,y); 
			printf("%I64d\n",min(v1,min(v2,v3)));
		}
	}
}
int main(){
	scanf("%d",&T);
	while(T--){
		init();
		dfs(1);
		dfs(1,1);
		solve();
	}
	return 0;
}

复习:
树剖:任意一个节点到根的路径上不超过Log条轻边和重链.
因为轻儿子的大小<父节点的一半,往上走,子树大小至少变为2倍.重链是间断的,个数不会超过轻边+1.
 

猜你喜欢

转载自blog.csdn.net/noone0/article/details/81980633