线段树优化 dijkstra

本文介绍了如何使用线段树数据结构优化Dijkstra算法,处理修改点距离、查询最小未取距离和标记节点的操作,通过实例演示了如何在区间查询和单点修改场景下实现高效求解最短路径问题。适合初学者理解复杂图算法的高级应用。
摘要由CSDN通过智能技术生成

学习博客

P4779 【模板】单源最短路径(标准版)
学习新东西肯定先从最简单的开始,先上最短路板子题
思路:
题解博客
线段树优化 Dijkstra
我们需要支持三个操作:

  • 修改某个点的 d i s dis dis
  • 找到一个没有拿出来过的 d i s dis dis 最小的点
  • 标记某个点已经被拿出来过

于是可以用线段树维护 d i s dis dis 数组的区间最小值和这个最小值对应的位置。这样子如果一个点已经被拿出来过,我们直接在线段树上把它改成 + ∞ +\infty +,这样子它之后就不会被拿出来了。
区间查询,单点修改
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
#define ls (p << 1)
#define rs (p << 1 | 1)
using namespace std;
const int maxn = 2e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, m, s;
int head[maxn], cnt, dis[maxn];
struct Edge{
	int to, next, w;
}e[maxn];

void add(int x, int y, int z){
	e[++cnt].w = z;
	e[cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

int tr[maxn << 2], pos[maxn << 2];

void pushup(int p){
	if(tr[ls] <= tr[rs]) tr[p] = tr[ls], pos[p] = pos[ls];
	else tr[p] = tr[rs], pos[p] = pos[rs];
}
void build(int p = 1, int cl = 1, int cr = n)
{
	if(cl == cr){
		tr[p] = dis[cl]; pos[p] = cl; return;
	}
	int mid = (cl + cr) >> 1;
	build(ls, cl, mid);
	build(rs, mid + 1, cr);
	pushup(p);
}
void update(int l, int r, int w, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r){
		tr[p] = w; return;
	}
	int mid = (cl + cr) >> 1;
	if(mid >= l) update(l, r, w, ls, cl, mid);
	if(mid < r) update(l, r, w, rs, mid + 1, cr);
	pushup(p);
}

void dij(int s){
	memset(dis, 0x3f, sizeof(dis));
	dis[s] = 0;
	build();
	while(tr[1] != inf)// 相当于 qry(1, n)
	{
		int now = pos[1];  update(now, now, inf);
		for(int i = head[now]; i; i = e[i].next)
		{
			int to = e[i].to;
			if(dis[to] > dis[now] + e[i].w)
				dis[to] = dis[now] + e[i].w, update(to, to, dis[to]);
		}
	}
}
void work()
{
	cin >> n >> m >> s;
	for(int i = 1, x, y, z; i <= m; ++i){
		cin >> x >> y >> z;
		add(x, y, z);
	}
	dij(s);
	for(int i = 1; i <= n; ++i) cout << dis[i] << " ";
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

例题:
D. Legacy
题意:
给你三种操作

  • 点到点建边
  • 点到区间建边
  • 区间到点建边

最后求起点到其他点的最短路,到不了的点输出 − 1 -1 1
思路:
题解
博主讲的很好很详细,认真读完就能明白了
p o s pos pos 数组用来记录叶子节点的编号
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
#define ls (p << 1)
#define rs (p << 1 | 1)
using namespace std;
const int maxn = 1e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m, s;
int trup[maxn << 2], trdown[maxn << 2];
struct Edge{
	int to, next, w;
}e[maxn << 5];
int head[maxn << 2], cnt, tot;
bool vis[maxn << 2];
ll dis[maxn << 2];
int posup[maxn << 2], posdown[maxn << 2];

void add(int x, int y, int z){
	e[++cnt].to = y;
	e[cnt].w = z;
	e[cnt].next = head[x];
	head[x] = cnt;
}
void build_up(int p = 1, int cl = 1, int cr = n)// 从下往上建边 
{
	trup[p] = ++tot;
	if(cl == cr){
		posup[cl] = trup[p]; return;
	}
	int mid = (cl + cr) >> 1;
	build_up(ls, cl, mid); build_up(rs, mid + 1, cr);
	add(trup[ls], trup[p], 0); add(trup[rs], trup[p], 0);
}
void build_down(int p = 1, int cl = 1, int cr = n)// 从上往下建边 
{
	trdown[p] = ++tot;
	if(cl == cr){
		posdown[cl] = trdown[p];
		add(trdown[p], posup[cl], 0);
		return;
	}
	int mid = (cl + cr) >> 1;
	build_down(ls, cl, mid); build_down(rs, mid + 1, cr);
	add(trdown[p], trdown[ls], 0); add(trdown[p], trdown[rs], 0);
}
void update(int op, int l, int r, int to, int d, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r){
		if(op == 3) add(trup[p], posdown[to], d);
		else add(posup[to], trdown[p], d);
		return;
	}
	int mid = (cl + cr) >> 1;
	if(mid >= l) update(op, l, r, to, d, ls, cl, mid);
	if(mid < r) update(op, l, r, to, d, rs, mid + 1, cr);
}
void dij(int s){
	memset(dis, 0x3f, sizeof(dis));
	dis[s] = 0;
	priority_queue<pair<ll,int>,vector<pair<ll,int> >, greater<pair<ll,int> > > q;
	q.push({0, s});
	while(!q.empty())
	{
		int x = q.top().second;q.pop();
		if(vis[x]) continue;
		vis[x] = 1;
		for(int i = head[x]; i; i = e[i].next)
		{
			int to = e[i].to;
			if(!vis[to] && dis[to] > dis[x] + e[i].w)
			{
				dis[to] = dis[x] + e[i].w;
				q.push({dis[to], to});
			}
		}
	}
}
void work()
{
	cin >> n >> m >> s;
	build_up();
	build_down();
	while(m--)
	{
		int op, l, r, x, y;ll w;
		cin >> op;
		if(op == 1){
			cin >> x >> y >> w;
			add(posup[x], posdown[y], w);
		}
		else{
			cin >> x >> l >> r >> w;
			update(op, l, r, x, w);
		}
	}
	dij(posup[s]);
	for(int i = 1; i <= n; ++i)
		cout << (dis[posup[i]] == INF ? -1 : dis[posup[i]]) << " ";
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

bzoj–3073—Journeys—vj链接

P1186 玛丽卡

hdu–5669–Road

这题可以考虑线段树建图
这题思维中为了保持区间连通性建长度为 0 0 0 的边也和线段树建图的一些地方类似

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值