HDU - 6393 Traffic Network in Numazu 树上倍增求lca+dfs序+线段树

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6393

题意:n个点n条边的图,求两点间的最小距离

题解:如果是一个树的话,那么我们就可以倍增+dfs序,然后用线段树维护下就可以了这样是nlogn的复杂度,当然也可以剖分一下,这样就是nlognlogn的复杂度了,都是可以的,但这个是n条边,那么就会有一个环,那么两点间的距离就会存在两个的情况,因此我们先拆除环上的一条边(u-v)的来,剩下的n-1条就按照之前的方法求,那么假设求x到y的距离,那么还需要判断一下x - u - v - y 和 x - v - u - y。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct edge {
	int to, nex, d,id;
}e[N << 1];
struct EDGE {
	int x, y, z;
}E[N];
int n, m;
int head[N], len;
int vis[N];
ll dis[N];
int dp[N][22];
int dep[N];
int in[N], out[N];
int pid[N];
int tot;
void add(int x,int y,int z,int id){
	e[len].to=y;
	e[len].d=z;
	e[len].id=id;
	e[len].nex=head[x];
	head[x]=len++;
}
void init(){
	len=0;
	tot = 0;
	for(int i=0;i<=n;i++){
		head[i]=-1;
		vis[i]=0;
		for(int j=0;j<22;j++)
			dp[i][j]=0;
	}
}
int flagu,flagv,flagid;
void dfs1(int u,int fa){
	if(flagu!=-1)return;
	vis[u]=1;
	int to;
	for(int i=head[u];i!=-1;i=e[i].nex){
		to=e[i].to;
		if(to==fa)continue;
		if(vis[to]&&flagu==-1){
			flagu=u;
			flagv=to;
			flagid=e[i].id;
			return;
		}
		dfs1(to,u);
	}
}
void dfs2(int u,int fa){
	int to;
	dep[u] = dep[fa] + 1;
	in[u] = ++tot;
	pid[tot] = u;
	for(int i = head[u]; i != -1; i = e[i].nex) {
		to = e[i].to;
		if(to == fa) continue;
		if(e[i].id == flagid) continue;
		dis[to] = dis[u] + e[i].d;
		dp[to][0] = u;
		for(int j = 1; j < 22; j++)
			if(dp[to][j - 1])
				dp[to][j] = dp[dp[to][j - 1]][j - 1];
			else
				break;
		dfs2(to, u);
	}
	out[u] = tot;
}
struct node {
	int l, r;
	ll val, laz;
}tree[N << 2];
void build(int l, int r, int cur) {
	tree[cur].l = l;
	tree[cur].r = r;
	tree[cur].laz = 0;
	tree[cur].val = 0;
	if(l == r) {
		tree[cur].val = dis[pid[l]];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, cur << 1);
	build(mid + 1, r, cur << 1 | 1);
}
void pushdown(int cur) {
	if(tree[cur].laz) {
		tree[cur << 1].laz += tree[cur].laz;
		tree[cur << 1].val += tree[cur].laz;
		tree[cur << 1 | 1].laz += tree[cur].laz;
		tree[cur << 1 | 1].val += tree[cur].laz;
		tree[cur].laz = 0;
	}
}
void update(int pl, int pr, int cur, ll val) {
	if(pl <= tree[cur].l && tree[cur].r <= pr) {
		tree[cur].laz += val;
		tree[cur].val += val;
		return;
	}
	pushdown(cur);
	if(pl <= tree[cur << 1].r) update(pl, pr, cur << 1, val);
	if(pr >= tree[cur << 1 | 1].l) update(pl, pr, cur << 1 | 1, val);
}
ll query(int pos, int cur) {
	if(tree[cur].l == tree[cur].r) {
		return tree[cur].val;
	}
	pushdown(cur);
	if(pos <= tree[cur << 1].r) return query(pos, cur << 1);
	else return query(pos, cur << 1 | 1);
}
int getlca(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	int cnt = dep[x] - dep[y];
	for(int i = 0; i < 22; i++) {
		if((cnt >> i) & 1)
			x = dp[x][i];
	}
	if(x == y) return x;
	for(int i = 21; i >= 0; i--) {
		if(dp[x][i] != dp[y][i]) {
			x = dp[x][i];
			y = dp[y][i];
		}
	}
	return dp[x][0];
}
int main() {
	int T;
	int x, y, z;
	int op;
	ll ans1, ans2, ans3;
	scanf("%d", &T);
	while(T--) {
		scanf("%d %d", &n, &m);
		init();
		for(int i = 1; i <= n; i++) {
			scanf("%d %d %d",&x, &y, &z);
			add(x,y,z,i);
			add(y,x,z,i);
			E[i].x = x;
			E[i].y = y;
			E[i].z = z;
		}
		flagu=-1;
		dfs1(1,0);
		dfs2(1,0);
		build(1, n, 1);
		while(m--) {
			scanf("%d %d %d", &op, &x, &z);
			if(op == 0) {
				if(x == flagid) {
					E[flagid].z = y;
				} else {
					if(dp[E[x].x][0] == E[x].y) {
						y = E[x].x;
					} else {
						y = E[x].y;
					}
					update(in[y], out[y], 1, z - E[x].z);
					E[x].z = z;
				}
			} else {
				dis[x] = query(in[x], 1);
				dis[z] = query(in[z], 1);
				dis[flagu] = query(in[flagu], 1);
				dis[flagv] = query(in[flagv], 1);
				ans1 = dis[x] + dis[z] - 2 * dis[getlca(x, z)];
				ans2 = dis[x] + dis[flagu] - 2 * dis[getlca(x, flagu)] + dis[z] + dis[flagv] - 2 * dis[getlca(z, flagv)] + E[flagid].z;
				ans3 = dis[x] + dis[flagv] - 2 * dis[getlca(x, flagv)] + dis[z] + dis[flagu] - 2 * dis[getlca(z, flagu)] + E[flagid].z;
				printf("%lld\n", min(ans1, min(ans2, ans3)));
			}
		}
	}
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值