迷宫花坛(garden)【仙人掌】【tarjan】【LCA】

>Description
圣玛格丽特学园的一角有一个巨大、如迷宫般的花坛。大约有一个人这么高的大型花坛,做成迷宫的形状,深受中世纪贵族的喜爱。维多利加的小屋就坐落在这迷宫花坛的深处。某一天早晨,久城同学要穿过这巨大的迷宫花坛,去探望感冒的维多利加。

整个迷宫可以用N个路口与M条连接两个不同路口的无向通道来描述。路口被标号为1到N,每条通道有各自的长度。整个迷宫一定是连通的,迷宫中可能存在若干个环路,但是,出于美观考虑,每个路口最多只会属于一个简单环路。例如,图1所示的迷宫是非常美观的,但图2则不符合我们的描述,因为3号路口同属于两个简单环。
在这里插入图片描述你需要回答多个这样的询问:假如久城处在路口x,维多利加的小屋处在路口y,久城最短需要走多少距离才能到达小屋?


>Input
第一行2个整数N,M,表示迷宫花坛的路口数和通道数;
接下来N行,每行3个整数x,y,z,描述一条连接路口x与路口y,长度为z的通道;
再接下来1行包含一个整数Q,表示询问数量;
之后Q行,每行2个整数x,y,描述一个询问。

>Output
对于每个询问输出一行一个整数,表示最短距离。

对于30%的数据,N≤100;
另有30%的数据,保证N=M;
对于100%的数据,1≤N≤100,000,Q≤200,000,1≤x,y≤N,1≤z≤1000.


>解题思路
一道仙人掌裸题


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 200005
#define int long long
using namespace std;

struct line
{
	int to, next, c;
} a[400005], aa[400005];
int n, m, q, t, h[N], tt, hh[N], dfn[N], low[N], w, fa[N], k[N], cnt;
int sum[N], dep[N], lg[N], f[N][50], dis[N], X, Y;

int read ()
{
	int l = 0;
	char cc = getchar();
	while (cc > '9' || cc < '0') cc = getchar();
	while (cc >= '0' && cc <= '9')
	{
		l = l * 10 + cc - '0';
		cc = getchar();
	}
	return l;
}
void write (int l)
{
	if (l > 9) write (l / 10);
	putchar (l % 10 + '0');
}
void add (int u, int v, int l)
{
	a[++t] = (line) {v, h[u], l}; h[u] = t;
	a[++t] = (line) {u, h[v], l}; h[v] = t;
}
void addd (int u, int v, int l)
{
	aa[++tt] = (line) {v, hh[u], l}; hh[u] = tt;
	aa[++tt] = (line) {u, hh[v], l}; hh[v] = tt;
}
void build (int u, int v, int l)
{
	cnt++;
	int x = v, s = l;
	while (x != fa[u])
	{
		sum[x] = s;
		s += k[x];
		x = fa[x];
	}
	sum[cnt] = sum[u];
	x = v;
	while (x != fa[u])
	{
		addd (x, cnt, min (sum[x], sum[cnt] - sum[x]));
		x = fa[x];
	}
}
void tarjan (int now)
{
	dfn[now] = low[now] = ++w;
	for (int i = h[now]; i; i = a[i].next)
	  if (a[i].to != fa[now])
	  {
	  	if (!dfn[a[i].to])
	  	{
	  		fa[a[i].to] = now;
	  		k[a[i].to] = a[i].c;
	  		tarjan (a[i].to);
	  		low[now] = min (low[now], low[a[i].to]);
	  	}
	  	else low[now] = min (low[now], dfn[a[i].to]);
	  	if (low[a[i].to] > dfn[now]) addd (now, a[i].to, a[i].c);
	  }
	for (int i = h[now]; i; i = a[i].next)
	  if (dfn[a[i].to] > dfn[now] && now != fa[a[i].to])
	    build (now, a[i].to, a[i].c);
}
void dfs (int now, int fath)
{
	dep[now] = dep[fath] + 1;
	f[now][0] = fath;
	for (int i = 1; i <= lg[dep[now]] - 1; i++)
	  f[now][i] = f[f[now][i - 1]][i - 1];
	for (int i = hh[now]; i; i = aa[i].next)
	  if (aa[i].to != fath)
	  {
	  	if (!dis[aa[i].to]) dis[aa[i].to] = dis[now] + aa[i].c;
	  	else dis[aa[i].to] = min (dis[aa[i].to], dis[now] + aa[i].c);
	  	dfs (aa[i].to, now);
	  }
}
int lca (int x, int y)
{
	if (dep[x] < dep[y]) swap (x, y);
	while (dep[x] > dep[y])
	  x = f[x][lg[dep[x] - dep[y]] - 1];
	if (x == y) return y;
	for (int i = lg[dep[x]] - 1; i >= 0; i--)
	  if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	X = x, Y = y;
	return f[x][0];
}
signed main()
{
	n = read (), m = read ();
	cnt = n;
	int u, v, l;
	for (int i = 1; i <= m; i++)
	{
		u = read (), v = read (), l = read ();
		add (u, v, l);
	}
	tarjan (1);
	
	for (int i = 1; i <= cnt; i++)
	  lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
	dfs (1, 0);
	
	q = read ();
	int x, y, z, ans = 0;
	for (int i = 1; i <= q; i++)
	{
		x = read (), y = read ();
		z = lca (x, y);
		if (z <= n) ans = dis[x] - dis[z] + dis[y] - dis[z];
		else
		{
			ans = dis[x] - dis[X] + dis[y] - dis[Y];
			if (sum[X] > sum[Y]) swap (X, Y);
			ans += min (sum[Y] - sum[X], sum[z] - (sum[Y] - sum[X]));
		}
		write (ans); putchar (10);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全评估测试题大模型安全评估测试题关键词库生成内容测试题库应拒答测试题库非拒答测试题大模型安全
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值