【图论经典题目讲解】CF786B - Legacy 一道线段树优化建图的经典题目

这篇具有很好参考价值的文章主要介绍了【图论经典题目讲解】CF786B - Legacy 一道线段树优化建图的经典题目。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C F 786 B − L e g a c y \mathrm{CF786B - Legacy} CF786B−Legacy

D e s c r i p t i o n \mathrm{Description} Description

给定 1 1 1 n n n 个点的有向图,初始没有边,接下来有 q q q 次操作,形式如下:

  • 1 u v w 表示从 u u u v v v 连接 1 1 1 条长度为 w w w 的有向边
  • 2 u l r w 表示从 u u u i i i i ∈ [ l , r ] i\in [l,r] i[l,r])连接 1 1 1 条长度为 w w w 的有向边
  • 3 u l r w 表示从 i i i i ∈ [ l , r ] i\in [l,r] i[l,r])向 u u u 连接 1 1 1 条长度为 w w w 的有向边

输出从 S S S 点到 i i i 点( i ∈ [ 1 , n ] i\in [1,n] i[1,n])的最短路长度。

S o l u t i o n \mathrm{Solution} Solution

观察可知,最多会建立 1 0 5 × 1 0 5 = 1 0 10 10^5\times 10^5 = 10^{10} 105×105=1010 条边,故必定超时。

此时,需要使用 线段树优化建图,这里展开简单说一下:

对于 1 1 1 棵存储点为 1 ∼ 4 1\sim 4 14 的线段树,形式如下:

【图论经典题目讲解】CF786B - Legacy 一道线段树优化建图的经典题目,图论经典,图论,c++,算法

如果当前为 2 2 2 操作,且为 1 ∼ 3 1\sim 3 13 每个点连向 4 4 4,权值为 10 10 10,操作如下所示:

【图论经典题目讲解】CF786B - Legacy 一道线段树优化建图的经典题目,图论经典,图论,c++,算法

即,将区间 1 ∼ 2 1\sim 2 12 3 ∼ 3 3\sim 3 33 连向 4 4 4 即可,不过此时发现,图中为有向图,而现在是无向图所以我们要对于图中的每一条边标记方向和权值(这里线段树就是一张图,叶子节点就是我们的 1 ∼ n 1\sim n 1n 节点)

【图论经典题目讲解】CF786B - Legacy 一道线段树优化建图的经典题目,图论经典,图论,c++,算法

其中,为何线段树上的边方向都为向父亲节点?那是因为 1 1 1 2 2 2 号点只有这样才能顺着边走到 4 4 4 号节点,对于为何权值设为 0 0 0,因为这是 1 1 1 条虚边(不存在的),不能对最短路做出任何贡献。

不过,上文是区间连节点,当是节点连区间的时候(操作 3 3 3)边都是正好反着的,所以再建 1 1 1 棵线段树即可(不过没必要真的去再建 1 1 1 棵,具体见代码)文章来源地址https://www.toymoban.com/news/detail-830504.html

C o d e Code Code

#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int SIZE = 4e6 + 10, SIZE2 = 1e6 + 10;

int N, Q, S;
int h[SIZE2], e[SIZE], ne[SIZE], w[SIZE], idx;
int Id[2], Dist[SIZE2], Vis[SIZE2];
struct Segment
{
	int l, r;
	int L, R;
}Tree[SIZE2 << 2];

void add(int a, int b, int c)
{
	e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}

int Build(int l, int r, int Sd, int k)
{
	if (l == r)
	{
		Tree[l] = {l, l};
		return l;
	}
	int P = ++ Id[k];
	Tree[P] = {l, r};

	int mid = l + r >> 1;
	Tree[P].L = Build(l, mid, Sd, k), Tree[P].R = Build(mid + 1, r, Sd, k);

	if (!Sd) add(Tree[P].L, P, 0), add(Tree[P].R, P, 0);
	else add(P, Tree[P].L, 0), add(P, Tree[P].R, 0);

	return P;
}

void Add(int u, int l, int r, int p, int w, int Sd)
{
	if (Tree[u].l >= l && Tree[u].r <= r)
	{
		if (!Sd) add(u, p, w);
		else add(p, u, w);
		return;
	}

	int mid = Tree[u].l + Tree[u].r >> 1;
	if (mid >= l) Add(Tree[u].L, l, r, p, w, Sd);
	if (mid < r) Add(Tree[u].R, l, r, p, w, Sd);
}

void Dijkstra(int S)
{
	memset(Dist, 0x3f, sizeof Dist);
	memset(Vis, 0, sizeof Vis);
	priority_queue<PII, vector<PII>, greater<PII>> Heap;
	Heap.push({0, S}), Dist[S] = 0;

	while (Heap.size())
	{
		auto Tmp = Heap.top();
		Heap.pop();

		int u = Tmp.second;
		if (Vis[u]) continue;
		Vis[u] = 1;

		for (int i = h[u]; ~i; i = ne[i])
		{
			int j = e[i];
			if (Dist[j] > Dist[u] + w[i])
			{
				Dist[j] = Dist[u] + w[i];
				Heap.push({Dist[j], j});
			}
		}
	}
}

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	memset(h, -1, sizeof h);

	cin >> N >> Q >> S;

	if (N == 1)
	{
		cout << 0 << endl;
		return 0;
	}

	Id[0] = N;
	Build(1, N, 0, 0);
	Id[1] = Id[0];
	Build(1, N, 1, 1);

	while (Q --)
	{
		int Op, v, u, l, r, w;
		cin >> Op >> u;

		if (Op == 1)
		{
			cin >> v >> w;
			add(u, v, w);
		}
		else if (Op == 2)
		{
			cin >> l >> r >> w;
			Add(Id[0] + 1, l, r, u, w, 1);
		}
		else
		{
			cin >> l >> r >> w;
			Add(N + 1, l, r, u, w, 0);
		}
	}

	Dijkstra(S);

	for (int i = 1; i <= N; i ++)
		if (Dist[i] >= 1e18) cout << -1 << " ";
		else cout << Dist[i] << " ";

	return 0;
}

到了这里,关于【图论经典题目讲解】CF786B - Legacy 一道线段树优化建图的经典题目的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CF786题解

    我不会告诉你链接在图片里 给出一个大小为 (n) 的环,点顺时针从 (1to n) 编号,两个人(设为 (0,1) )轮流移动其中的一个棋子。 对于第 (opt) 人,他能够将这个棋子顺时针移动 (xin S_{opt}) ( (S_{opt}) 是提前给出的)个步数,当某个人将棋子挪到 (1) 时这个人获胜。

    2024年02月05日
    浏览(28)
  • 【*2200线段树Pushup】CF1567 E

    Problem - E - Codeforces 题意: 思路: 维护这些信息即可    Code:

    2024年02月16日
    浏览(23)
  • 【枚举区间+线段树】CF Ehu 152 E

    Problem - E - Codeforces 题意: 思路: 感觉是个套路题 对区间计数,按照CF惯用套路,枚举其中一个端点,对另一个端点计数 对于这道题,枚举右端点,对左端点计数 Code:  

    2024年02月10日
    浏览(25)
  • 贪心找性质+dp表示+矩阵表示+线段树维护:CF573D

    比较套路的题目 首先肯定贪心一波,两个都排序后尽量相连。我一开始猜最多跨1,但其实最多跨2,考虑3个人的情况: 我们发现第3个人没了,所以可以出现跨2的情况 然后直接上dp,由 i − 1 , i − 2 , i − 3 i-1,i-2,i-3 i − 1 , i − 2 , i − 3 转移过来。 然后这显然可以拿矩阵表

    2024年02月07日
    浏览(27)
  • 【*1900 图论】CF1328 E

    Problem - E - Codeforces 题意: 思路: 注意到题目的性质:满足条件的路径个数是极少的,因为每个点离路径的距离 =1 先考虑一条链,那么直接就选最深那个点作为端点即可 为什么,因为我们需要遍历所有点的父亲 推广到树,也是要遍历所有点的父亲 为什么要加枚举的tag,因为

    2024年02月15日
    浏览(23)
  • 【简单图论】CF1833 E

    Problem - E - Codeforces 题意:    思路: 显然,最大值就是什么边都不连的连通块个数,最小值就是能连的都连上 那就是,如果一个连通块存在度为1的点,就把它当作接口连接 Code:

    2024年02月16日
    浏览(27)
  • 【*1900 图论+枚举思想】CF1328 E

    Problem - E - Codeforces 题意: 思路: 注意到题目的性质:满足条件的路径个数是极少的,因为每个点离路径的距离 =1 先考虑一条链,那么直接就选最深那个点作为端点即可 为什么,因为我们需要遍历所有点的父亲 推广到树,也是要遍历所有点的父亲 为什么要加枚举的tag,因为

    2024年02月14日
    浏览(28)
  • 【简单图论】CF898 div4 H

    Problem - H - Codeforces 题意: 思路: 手玩一下样例就能发现简单结论: v 离它所在的树枝的根的距离 m 离这个根的距离时是 YES 否则就是NO 实现就很简单,先去树上找环,然后找出这个根,分别给a 和 b BFS一遍,得出两个dis数组,比较一下即可 对于只有的环情况 和 m = v 的情况需

    2024年02月07日
    浏览(33)
  • 每天一道leetcode:542. 01 矩阵(图论&中等&广度优先遍历)

    给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 m == mat.length n == mat[i].length 1 = m, n = 104 1 = m * n = 104 mat[i][j] is either 0 or 1. mat 中至少有一个 0 找到距离当前位置最近的0,有

    2024年02月11日
    浏览(28)
  • 每天一道leetcode:934. 最短的桥(图论&中等&广度优先遍历)

    给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地, 0 表示水域。 岛 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。 grid 中 恰好存在两座岛 。 你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛 。 返回必须翻转的 0 的最小数

    2024年02月12日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包