[CF 1051F] The Shortest Statement

2 篇文章 0 订阅
1 篇文章 0 订阅

题目

洛谷

题意

有一张 n n n 个点 m m m 条边的无向图,有 q q q 个询问,输出 u u u v v v 的最短路长度。

思路

首先, n 、 m 、 q n、m、q nmq < = 1 e 5 <=1e5 <=1e5。直接每次跑最短路是肯定要 T L E \tt TLE TLE 的。然而,我们需要注意数据的特性: m − n < = 20 m-n<=20 mn<=20。也就是说,我们可以把这张图看为:首先有一棵树,然后在树上加 < = 20 <=20 <=20 条边。

我们考虑,若只是一棵树,直接 L C A \tt LCA LCA 就OK了。所以先做一次最小生成树,得到一棵树。这个时候,我们考虑加边:那么这时候就很简单了:若是求 u u u v v v 之间的最短路,要么从树上走,要么通过加入的边走。那么我们只需要将加入的边 f r o m from from t o to to 跑一下到每个点的最短路。最后得到 < = 40 <=40 <=40 个端点,与树上最短路径比较一下即可。

code

struct node
{
	LL v, w;
	node(){}
	node(LL V,LL W)
	{
		v = V;
		w = W;
	}
	friend bool operator <(node A,node B)
	{
		return A.w > B.w;
	}
};
vector<node> NG[MAXN], G[MAXN];
struct Edge
{
	LL u, v, w, Index;
	Edge(){}
	Edge(LL U,LL V,LL W,LL INDEX)
	{
		u = U;
		v = V;
		w = W;
		Index = INDEX;
	}
	friend bool operator <(Edge A,Edge B)
	{
		return A.w < B.w;
	}
}Eg[MAXN], Ks[MAXN];
bool Used[MAXN];
LL n, m, S, T, Fa[MAXN];
inline void MakeSet(LL x)
{
	for (Int i = 1; i <= x; ++ i)
		Fa[i] = i;
}
LL FindSet(LL x)
{
	if (Fa[x] != x)
		Fa[x] = FindSet( Fa[x] );
	return Fa[x];
}
inline void Kruscal()
{
	LL MST = 0;
	MakeSet( n );
	for (Int i = 1; i <= m && MST < n - 1; ++ i)
	{
		LL Fu = FindSet( Ks[i].u );
		LL Fv = FindSet( Ks[i].v );
		if (Fu == Fv)
			continue;
		NG[Ks[i].u].push_back(node(Ks[i].v, Ks[i].w));
		NG[Ks[i].v].push_back(node(Ks[i].u, Ks[i].w));  
		Fa[Fu] = Fv;
		MST ++;
		Used[Ks[i].Index] = 1;
	}
}
LL Dep[MAXN], Depp[MAXN], f[MAXN][MAXK];
void Build(LL x,LL Pre,LL Bq)
{
	Dep[x] = Dep[Pre] + 1;
	Depp[x] = Depp[Pre] + Bq;
	f[x][0] = Pre;
	for (Int i = 1; i < MAXK; ++ i)
		f[x][i] = f[f[x][i - 1]][i - 1];
	int l = NG[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = NG[x][i].v;
		if (to == Pre)
			continue;
		Build(to, x, NG[x][i].w);
	}
}
inline void Adjust(LL &u,LL Tar)
{
	for (Int i = 19; i >= 0; -- i)
		if (Dep[f[u][i]] >= Tar)
			u = f[u][i];
}
inline LL LCA(LL u,LL v)
{
	if (Dep[u] > Dep[v])
		Adjust(u, Dep[v]);
	else if (Dep[v] > Dep[u])
		Adjust(v, Dep[u]);
	if (u == v)
		return (u + v) / 2;
	for (Int i = 19; i >= 0; -- i)
		if (f[u][i] != f[v][i])
			u = f[u][i], v = f[v][i];
	return f[u][0];
}
bool Pao[MAXN];
LL Dis[MAXM][MAXN];
priority_queue<node> Q;
inline void Dijkstra(LL Now,LL Bh)
{
	Q.push(node(Now, 0));
	Dis[Bh][Now] = 0;
	while (! Q.empty())
	{
		node Pop = Q.top(); Q.pop();
		LL x = Pop.v;
		int l = G[Pop.v].size();
		for (Int i = 0; i < l; ++ i)
		{
			LL to = G[Pop.v][i].v, Val = G[Pop.v][i].w;
			if (Dis[Bh][x] + Val < Dis[Bh][to])
			{
				Dis[Bh][to] = Dis[Bh][x] + Val;
				Q.push(node(to, Dis[Bh][to]));
			}
		}
	}
}
LL Had[MAXN], tot;
map<LL, LL> Map;
map<LL, LL>::iterator item;
int main()
{
	read( n ); read( m );
	for (Int i = 1; i <= m; ++ i)
	{
		LL u, v, w;
		read( u ); read( v ); read( w );
		G[u].push_back(node(v, w));
		G[v].push_back(node(u, w));
		Ks[i] = Eg[i] = Edge(u, v, w, i);
	}
	sort(Ks + 1, Ks + 1 + m);
	Kruscal();
	Build(1, 0, 0);
	for (Int i = 1; i <= m; ++ i)
		if (! Used[i])
		{
			if (! Map[Eg[i].u])
			{
				Map[Eg[i].u] = ++ tot;
				for (Int j = 1; j <= n; ++ j)
					Dis[Map[Eg[i].u]][j] = INF;
				Dijkstra(Eg[i].u, Map[Eg[i].u]);
			}
			if (! Map[Eg[i].v])
			{
				Map[Eg[i].v] = ++ tot;
				for (Int j = 1; j <= n; ++ j)
					Dis[Map[Eg[i].v]][j] = INF;
				Dijkstra(Eg[i].v, Map[Eg[i].v]);
			}
		}
	LL Q;
	read( Q );
	while (Q --)
	{
		LL u, v;
		read( u ); read( v );
		LL Mid = LCA(u, v);
		LL Ans = Depp[u] + Depp[v] - Depp[Mid] * 2;
		for (Int i = 1; i <= tot; ++ i)
			Ans = Min(Ans, Dis[i][u] + Dis[i][v]);
		printf("%lld\n", Ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值