bzoj3694[FJOI 2014]最短路题解(超详细) 树链剖分

题目(洛谷上没有就懒得找了):给出一个 n 个点 m 条边的无向图, n 个点的编号从 1∼n , 定义源点为 1。定义最短路树如下:从源点 1 经过边集 T到任意一点 i 有且仅有一条路径, 且这条路径是整个图 1 到 i 的最短路径, 边集 T 构成最短路树。
给出最短路树, 求对于除了源点 1 外的每个点 i , 求最短路, 要求不经过给出的最短路树上的 1 到 i 的路径的最后一条边。
对于 100% 的数据, n≤4000,m≤100000,1≤li≤100000
emmm,n≤4000确定不考虑一发暴力?好吧,这道题只要你常数够好,卡过是完全没问题的(瞎说的,别去试,dijikstra堆优化是O((n + m)logn))。暴力很简单:枚举删边,每删一次,跑一边最短路,复杂度O(n * (n + m)logn)。我们顺着暴力的思路,考虑如何计算对于每一个点的最短路。首先我们可以发现,这里的最短路一定不能全由树边(树边:在最短路树中的边)组成,那么一定为根节点 -> 某一点(通过树边) -> 另一点(通过一条非树边,两条一定不是最优)->当前点(可以在上一步的时候就到,也可以由上一步通过一些反向树边到)。如图:(颜色较深的为树边)在这里插入图片描述
如更新节点2:由1->5->2或由1->5->4->2。
现在,我们知道了最短路是如何更新的,但,我们发现一个问题,复杂度过不去,我们总不能直接对每一个点枚举所有非树边吧。(复杂度O(n*m)只比暴力略优)这是我们想要优化这个复杂度,显然m是没有办法改变的(1.需要每一条非树边保证答案是最优 2.对于非树边,不具有可以整体操作的方法),那么我们对于n又该怎么优化呢?这时,我们转换一下思路,对于每一条非树边,它其实并不能更新所有点的最短路,若该边为(u,v),那么他只能更新(u -> lca(u , v) -> v)上的节点(易证,自己画一下图就明白了)。(u -> lca(u , v) -> v)?这是个好东西,不就是u -> v的路径上的点吗,看到这,想必大家都明白了,没错,就是树链剖分。
(不会的同志可以去看看我的博客,写得很详细,传送门:https://blog.csdn.net/weixin_43790474/article/details/84556647
那么,想到了树链剖分,这道题就很简单了。就是枚举非树边,然后,对于每条非树边,用d[u] + d[v] - d[lca(u , v)] - d[x]来更新路径上的点,既每个点维护d[u] + d[v] - d[lca(u , v)]的最小值,最后再减去d[x]就ok了。复杂度O(mlogn),代码如下:
感觉代码都是板子,就不写注释了吧。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 4010 , maxm = 200010;
int n , m;
ll inf;
struct edge
{
	int u , v , next;
	ll w;
}E[2 * maxn] , S[maxm];
int len , head[maxn];
void add(int u , int v , ll w)
{
	E[len].u = u , E[len].v = v , E[len].w = w , E[len].next = head[u];
	head[u] = len++;
}
inline char get_char()
{
	static char buf[1000000] , *p1 = buf , *p2 = buf;
	if (p1 == p2)
	{
		p2 = (p1 = buf) + fread(buf , 1 , 1000000 , stdin);
		if (p1 == p2)
		{
			return EOF;
		}
	}
	return *p1++;
}
inline ll read()
{
	ll res;
	char ch;
	ll f = 1;
	while (!isdigit(ch = get_char()))
	{
		if (ch == '-')
		{
			f = -1;
		}
	}
	res = ch - '0';
	while (isdigit(ch = get_char()))
	{
		res = res * 10 + ch - '0';
	}
	return res;
}
int fa[14][maxn] , son[maxn] , top[maxn] , sz[maxn] , dep[maxn] , tid[maxn];
ll d[maxn];
void dfs1(int u)
{
	sz[u] = 1;
	for (int i = head[u]; ~i; i = E[i].next)
	{
		int v = E[i].v;
		ll w = E[i].w;
		if (v != fa[0][u])
		{
			dep[v] = dep[u] + 1;
			fa[0][v] = u;
			d[v] = d[u] + w;
			dfs1(v);
			sz[u] += sz[v];
			if (son[u] == -1 || sz[v] > sz[son[u]])
			{
				son[u] = v;
			}
		}
	}
}
int cnt = 0;
void dfs2(int u , int Top)
{
	top[u] = Top;
	tid[u] = ++cnt;
	if (son[u] != -1)
	{
		dfs2(son[u] , Top);
	}
	for (int i = head[u]; ~i; i = E[i].next)
	{
		int v = E[i].v;
		if (v != fa[0][u] && v != son[u])
		{
			dfs2(v , v);
		}
	}
}
int lca(int x , int y)
{
	if (dep[x] < dep[y])
	{
		swap(x , y);
	}
	int i , j;
	for (i = 0; (1 << i) <= n; i++)
	{
		;
	}
	i--;
	for (j = i; j >= 0; j--)
	{
		if (dep[x] - (1 << j) >= dep[y])
		{
			x = fa[j][x];
		}
	}
	if (x == y)
	{
		return x;
	}
	for (j = i; j >= 0; j--)
	{
		if (fa[j][x] != fa[j][y])
		{
			x = fa[j][x];
			y = fa[j][y];
		}
	}
	return fa[0][x];
}
ll tree[4 * maxn] , lazy[4 * maxn];
void pushdown(int id)
{
	if (lazy[id] != inf)
	{
		lazy[id << 1] = min(lazy[id << 1] , lazy[id]);
		lazy[id << 1 | 1] = min(lazy[id << 1 | 1] , lazy[id]);
		tree[id << 1] = min(tree[id << 1] , lazy[id]);
		tree[id << 1 | 1] = min(tree[id << 1 | 1] , lazy[id]);
		lazy[id] = inf;
	}
}
void update(int id , int l , int r , int x , int y , ll val)
{
	if (l >= x && r <= y)
	{
		tree[id] = min(tree[id] , val);
		lazy[id] = min(lazy[id] , val);
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(id);
	if (x <= mid)
	{
		update(id << 1 , l , mid , x , y , val);
	}
	if (y > mid)
	{
		update(id << 1 | 1 , mid + 1 , r , x , y , val);
	}
	tree[id] = min(tree[id << 1] , tree[id << 1 | 1]);
}
void update_path(int x , int y , int t , ll val)
{
	int flag = 0;
	while (top[x] != top[y])
	{
		if (dep[top[x]] < dep[top[y]])
		{
			swap(x , y);
		}
		update(1 , 1 , n , tid[top[x]] , tid[x] , val);
		x = fa[0][top[x]];
	}
	if (dep[x] > dep[y])
	{
		swap(x , y);
	}
	update(1 , 1 , n , tid[son[x]], tid[y] , val);
}
ll query(int id , int l , int r , int pos)
{
	if (l == r && l == pos)
	{
		return tree[id];
	}
	int mid = (l + r) >> 1;
	pushdown(id);
	ll res = inf;
	if (mid >= pos)
	{
		res = min(res , query(id << 1 , l , mid , pos));
	}
	else
	{
		res = min(res , query(id << 1 | 1 , mid + 1 , r , pos));
	}
	return res;
}
int main()
{
	memset(head , -1 , sizeof(head));
	memset(son , -1 , sizeof(son));
	memset(tree , 0x7f , sizeof(tree));
	memset(lazy , 0x7f , sizeof(lazy));
	inf = lazy[0];
	n = read() , m = read();
	int num = 0;
	for (int i = 1; i <= m; i++)
	{
		int u , v , judge;
		ll w;
		u = read() , v = read() , w = read() , judge = read();
		if (judge == 1)
		{
			add(u , v , w);
			add(v , u , w);
		}
		else
		{
			S[++num].u = u , S[num].v = v , S[num].w = w;
		}
	}
	dfs1(1);
	dfs2(1 , 1);
	for (int level = 1; (1 << level) <= n; level++)
	{
		for (int i = 1; i <= n; i++)
		{
			fa[level][i] = fa[level - 1][fa[level - 1][i]];
		}
	}
	for (int i = 1; i <= num; i++)
	{
		int u = S[i].u , v = S[i].v;
		ll w = S[i].w;
		int t = lca(u , v);
		ll tmp = d[u] + w + d[v];
		update_path(u , v , t , tmp);
	}
	for (int i = 2; i <= n; i++)
	{
		ll ans = query(1 , 1 , n , tid[i]);
		if (ans != inf)
		{
			printf("%lld\n" , ans - d[i]);
		}
		else
		{
			printf("-1\n");
		}
	}
	return 0;
}

看完了别忘了点个赞再走哦。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值