codeforces786B Legacy 线段树优化建图

网址:https://codeforces.com/problemset/problem/786/B

题意:

给出$n$个城市和三种路径:$u$向$v$连一条带权无向边;$[l,r]$向$v$连一条带权无向边;$u$向$[l,r]$连一条带权无向边,给出一个起点$s$,求它到其他点的最短路径,如果不能到达,输出$-1$。

题解:

现在有三种操作:点对点连边,点对线段连边,线段对点连边。且点数多达$1e5$,所以就要建立线段树优化建图。建立入度树(就是边的终点)时,需要连上父节点往子节点的点且边权为$0$,建立出度树时,需要子节点往父节点连上边权为$0$的边。然后这里点对点,区间对点,点对区间连边,所以可以直接连边不需要超级结点,如果出现了区间对区间,就需要超级结点。然后连边之后在这个图上用一次$dijkstra$算法就行了。

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e5 + 5;
struct node
{
	int l, r;
};
node tr[MAXN];
struct edge
{
	int to;
	ll w;
	edge() {}
	edge(int _to, ll _w) :to(_to), w(_w) {}
	bool operator<(const edge& a)const
	{
		return w > a.w;
	}
};
vector<edge>G[MAXN];
int cnt = 0;
void buildout(int& rt, int l, int r)
{
	if (l == r)
	{
		rt = l;
		return;
	}
	rt = ++cnt;
	int m = (l + r) >> 1;
	buildout(tr[rt].l, l, m);
	buildout(tr[rt].r, m + 1, r);
	G[rt].push_back(edge(tr[rt].l, 0));
	G[rt].push_back(edge(tr[rt].r, 0));
}
void buildin(int& rt, int l, int r)
{
	if (l == r)
	{
		rt = l;
		return;
	}
	rt = ++cnt;
	int m = (l + r) >> 1;
	buildin(tr[rt].l, l, m);
	buildin(tr[rt].r, m + 1, r);
	G[tr[rt].l].push_back(edge(rt, 0));
	G[tr[rt].r].push_back(edge(rt, 0));
}
void update(int rt, int l, int r, int ql, int qr, int v, int w, int type)
{
	if (l <= ql && r >= qr)
	{
		type == 2 ? G[v].push_back(edge(rt, w)) : G[rt].push_back(edge(v, w));
		return;
	}
	int m = (ql + qr) >> 1;
	if (l <= m)
		update(tr[rt].l, l, r, ql, m, v, w, type);
	if (r > m)
		update(tr[rt].r, l, r, m + 1, qr, v, w, type);
}
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll dis[MAXN];
bool vis[MAXN];
priority_queue<edge>Q;
void dijkstra(int s)
{
	memset(vis, 0, sizeof(vis));
	memset(dis, 0x3f, sizeof(dis));
	dis[s] = 0;
	Q.push(edge(s, dis[s]));
	while (Q.size())
	{
		auto u = Q.top();
		Q.pop();
		if (vis[u.to])
			continue;
		vis[u.to] = 1;
		for (auto i : G[u.to])
		{
			int v = i.to;
			if (!vis[v] && dis[v] > dis[u.to] + i.w)
			{
				dis[v] = dis[u.to] + i.w;
				Q.push(edge(v, dis[v]));
			}
		}
	}
}
int rt1, rt2;
int main()
{
	int n, m, s;
	scanf("%d%d%d", &n, &m, &s);
	cnt = n + 1;
	buildin(rt2, 1, n);//区间出去
	buildout(rt1, 1, n);//区间进来
	int op, u, v, l, r, w;
	while (m--)
	{
		scanf("%d", &op);
		if (op == 1)
		{
			scanf("%d%d%d", &u, &v, &w);
			G[u].push_back(edge(v, w));
		}
		else
		{
			scanf("%d%d%d%d", &v, &l, &r, &w);
			update(op == 2 ? rt1 : rt2, l, r, 1, n, v, w, op);
		}
	}
	dijkstra(s);
	for (int i = 1; i <= n; ++i)
		printf("%lld%c", dis[i] >= INF ? -1 : dis[i], i == n ? ‘\n‘ : ‘ ‘);
	return 0;
}

原文地址:https://www.cnblogs.com/Aya-Uchida/p/12324350.html

时间: 2024-10-26 17:55:38

codeforces786B Legacy 线段树优化建图的相关文章

CF786B Legacy 线段树优化建图

问题描述 CF786B LG-CF786B 题解 线段树优化建图 线段树的一个区间结点代表 \([l,r]\) 区间点. 然后建立区间点的时候就在线段树上建边,有效减少点的个数,从而提高时空效率. 优质题解传送门 \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; #define int long long template <typename Tp> void read(Tp &x){ x=0;ch

786B - Legacy(线段树 + 最短路)线段树优化建图

题意: 就是给定一张n nn个点的图,求源点s ss到每个点的单源最短路.这张图共有q组边,连边方式有3种: a→b ,边权为w的单向边:a→[l,r] ,即a到连续区间[l,r]中的每一个点都有一条边权为w的边.[l,r]→a,即连续区间[l,r] 中的每一个点都有一条到a 边权为w 的边.注意数据范围n,q≤10^5. 题解 如果暴力连边(将一对多或者多对一的连边拆开分别连边),那么最差情况下,我们的边数将达到O(nq) 级别,连边就直接超出能承受的范围——更不要说再跑最短路了.因此我们考虑

线段树优化建图

建图时,当边数非常的多,连边就需要线段树优化复杂度.将 O(n2) 优化成 O(nlogn). 图来自生哥,非常感谢他. ? 2018/2/23 18:51:00 就这个样子 ? 2018/2/23 18:51:10 绿边容量INF ? 2018/2/23 18:51:17 红边容量1 ? 2018/2/23 18:51:39 蓝边是个节点 代码实现待续-- 原文地址:https://www.cnblogs.com/milky-w/p/8463205.html

[XSY 1129] flow 最小割 树链剖分 线段树优化建图

题意 给定一张 $N$ 个点的有根树, $1$ 为树的根. 每个点有点权 $V[i]$ . 若 $V[i] > 0$ , 则可以获得 $V[i]$ 的收益; 若 $V[i] < 0$ , 则需要付出 $V[i]$ 的代价. 如果选择了某个点 $x$ , 且它的连续的 $K$ 个祖先中存在一个没有选, 则需要付出 $P_x$ 的代价. 最大化总收益. 分析 将 树链剖分 + zkw线段树 嵌入网络中, 跑最小割. 实现 #include <cstdio> #include <c

Codeforces 787D. Legacy 线段树优化建图+最短路

output standard output Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them. There are n planets in their universe numbered from 1 to n.

uestc summer training #3 线段树优化建边

A 如果把边数缩小到n^2可以接受的话 就是一个最小点基的裸题 但是这里可能有n^2条边所以我们需要线段树优化建边 然后再求出SCC 扣掉不包含原始n个节点的SCC或者把除叶子节点外线段树上的点权设为inf 然后跑最小点基 #include<cstdio> #include<algorithm> #include<set> using namespace std; typedef pair<int, int> P; const int N = 1000010

CF793G Oleg and chess [线段树优化建边,扫描线,最大流]

不相交就直接搞啊..没啥技巧,i->j如果选了就是(i,j)选了. // powered by c++11 // by Isaunoya #include <bits/stdc++.h> #define rep(i, x, y) for (register int i = (x); i <= (y); ++i) #define Rep(i, x, y) for (register int i = (x); i >= (y); --i) using namespace std;

CF786B Legacy(线段树优化建图)

题意 有n个点,q个询问,每次询问有一种操作.操作1:u→[l,r](即u到l,l+1,l+2,...,r距离均为w)的距离为w:操作2:[l,r]→u的距离为w:操作3:u到v的距离为w:求起点到其他点的最短距离,到达不了输出-1. 题解 线段树骚操作,线段树优化建图. 其实提到可以这么操作后,实现还是很好想的. 建两颗线段树,一颗连进边,一颗连出边. 1 #include<iostream> 2 #include<cstdio> 3 #include<cmath>

【bzoj4276】[ONTAK2015]Bajtman i Okr?g?y Robin 线段树优化建图+费用流

题目描述 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢走c[i]元.作为保安,你在每一段长度为1的时间内最多只能制止一个强盗,那么你最多可以挽回多少损失呢? 输入 第一行包含一个正整数n(1<=n<=5000),表示强盗的个数. 接下来n行,每行包含三个正整数a[i],b[i],c[i](1<=a[i]<b[i]<=5000,1<=c[i]