Slipper(虚点+Dij)

题目来源:

2022“杭电杯”中国大学生算法设计超级联赛(5)

Slipper

大致题意:

一个树 有n个节点 编号分别为1到n 1号节点是根节点

节点间的边有边权

可以使用特殊跳跃 跳跃权值为p

使用特殊跳跃后 可以从当前节点跳到深度为 当前节点深度+k 的任意节点

问从s点到t点经过的权值和最小是多少

大致思路:

1.记录每个节点深度

用DFS跑图得到每个节点的深度

2.添加虚点

额外添加2*n个点 编号为n+1...n+n 和 2*n+1...2*n+n

添加边

第一组n+i用来离开i层树

树上深度为i的所有节点单向连向n+i号节点 deep:i → n+i

边权皆为0

第二组2*n+i用来进入i层树

树上深度为i的所有节点被2*n+i号节点单向连向 2*n+i → deep:i

边权皆为0

第一组和第二组之间 相差k的相连 用来实现跳跃 边权皆为p

注意:

只用一组原点 相隔k连双向边 会导致同深度的节点间距离错误的变为0

3.跑Dij

AC代码:

#include <bits/stdc++.h>
using namespace std;
using db = long double;
using ll = long long;
#define edl '\n'
#define str string
#define pll pair<ll, ll>
#define fir first
#define sec second
#define heap priority_queue
#define SPO(n) fixed << setprecision(n)
#define FOR(i, l, r) for (long long i = l; i <= r; ++i)
#define ROF(i, r, l) for (long long i = r; i >= l; --i)
#ifdef debugcmd
#define DBG(n) cout << "!!! " << #n << ": " << n << edl
#else
#define DBG(n) ;
#endif
// const long double PI = acos(-1.0);
// const long double EPS = 1.0e-9;
const int INF = 0x3f3f3f3f;
const long long LNF = 0x3f3f3f3f3f3f3f3f;
const long long MOD = 1e9 + 7;
const long long MXN = 3e6 + 5;
struct Dij_Graph
{
	ll edgeID;
	ll nxt[MXN << 1];
	ll head[MXN << 1];
	ll tail[MXN << 1];
	ll val[MXN << 1];
	ll dist[MXN];
	bool vis[MXN];
	heap<pll, vector<pll>, greater<pll>> he;
	void Init(void)
	{
		edgeID = 0;
		memset(head, -1, sizeof(head));
		return;
	}
	void AddEdge(ll u, ll v, ll w)
	{
		++edgeID;
		nxt[edgeID] = head[u];
		head[u] = edgeID;
		tail[edgeID] = v;
		val[edgeID] = w;
		return;
	}
	void Build(ll s)
	{
		memset(dist, INF, sizeof(dist));
		memset(vis, false, sizeof(vis));
		while (!he.empty())
			he.pop();
		dist[s] = 0;
		pll tmp;
		tmp.fir = 0;
		tmp.sec = s;
		he.push(tmp);
		while (!he.empty())
		{
			tmp = he.top();
			he.pop();
			ll id = tmp.sec;
			if (vis[id])
				continue;
			vis[id] = true;
			for (ll i = head[id]; i != -1; i = nxt[i])
			{
				ll j = tail[i];
				if (!vis[j] && dist[j] > dist[id] + val[i])
				{
					dist[j] = dist[id] + val[i];
					tmp.fir = dist[j];
					tmp.sec = j;
					he.push(tmp);
				}
			}
		}
		return;
	}
	ll Query(ll p)
	{
		if (dist[p] == LNF)
			return -1;
		return dist[p];
	}
};
Dij_Graph g;
ll deep[MXN];

void DFS(ll x, ll fa)
{
	for (ll i = g.head[x]; i != -1; i = g.nxt[i])
	{
		ll j = g.tail[i];
		if (j == fa)
			continue;
		deep[j] = deep[x] + 1;
		DFS(j, x);
	}
	return;
}
void Solve(void)
{
	ll n;
	cin >> n;
	g.Init();
	FOR(i, 1, n - 1)
	{
		ll u, v, w;
		cin >> u >> v >> w;
		g.AddEdge(u, v, w);
		g.AddEdge(v, u, w);
	}
	deep[1] = 1;
	DFS(1, -1);
	ll mxDeep = 1;
	FOR(i, 1, n)
	{
		g.AddEdge(i, deep[i] + n, 0);
		g.AddEdge(deep[i] + 2 * n, i, 0);
		mxDeep = max(mxDeep, deep[i]);
	}
	ll k, p;
	cin >> k >> p;
	FOR(i, 1, mxDeep)
	{
		if (i + k > mxDeep)
			break;
		g.AddEdge(n + i, 2 * n + i + k, p);
		g.AddEdge(n + i + k, 2 * n + i, p);
	}
	ll s, t;
	cin >> s >> t;
	g.Build(s);
	cout << g.Query(t) << edl;
	return;
}
int main(void)
{
	std::ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	// #ifdef cincoutcmd
	// freopen("cin.txt","r",stdin);
	// freopen("cout.txt","w",stdout);
	// #endif
	ll t;
	cin >> t;
	while (t--)
		Solve();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值