大致题意:给一个图,有3种边,t=1时,是点u对点v的单向边,t=2时,是点u到标号为[l,r]的点的单向边,t=3时,是标号为[l,r]到点u的边,求单源多汇最短路。
注意,点的数量为10^5,所以如果把每个点之间的边都存下来会占用GB级别的内存,显然这不是出题者所希望看到的,我们应该对存储边的方式进行调整。
既然我们不能把每个点对点的边都存下来,不如新建一些集散点用进行集中和分散,就像现实生活中的货运集散点一样,这样的话边的数量就会有所减少。
再观察题目给出边的特点,它虽然给出的边是一个点指向多个点的边,但是这些点的边都标号都是连续的。所以把标号相邻的点联向一个共同的集散点,然后将二者共同的边改为从这个集散点联向其他点,这样将会稳定且有效地减少边的数量。
但是对于新图,边数仍然不少,但我们仍然可以对新建的点重复上述步骤来增加集散点,直到所有点都联向了一个集散点。
注意,入边和出边的集散点应该分开来建。
在纸上模拟上述步骤,会发现这不就是两颗线段树吗?对,它的确和线段树相差无几。所以我们可以用线段树的建树函数和更新过程来进行构建新图。之于最短路就不是本题的主要问题了。
#include <bits/stdc++.h>
#define maxn 1700000
#define ll long long
#define mp make_pair
#define pb push_back
#define st first
#define ed second
using namespace std;
inline void read(int &x) {
char ch;
bool flag = false;
for (ch = getchar(); !isdigit(ch); ch = getchar())if (ch == '-') flag = true;
for (x = 0; isdigit(ch); x = x * 10 + ch - '0', ch = getchar());
x = flag ? -x : x;
}
inline void write(int x) {
static const int maxlen = 100;
static char s[maxlen];
if (x < 0) { putchar('-'); x = -x;}
if (!x) { putchar('0'); return; }
int len = 0; for (; x; x /= 10) s[len++] = x % 10 + '0';
for (int i = len - 1; i >= 0; --i) putchar(s[i]);
}
set< pair<ll, int> > s;
vector < pair<int, int> > v[maxn];
int n, m, st;
ll dist[maxn];
int num[maxn][2], tot;
//x=0代表入点,x=1代表出点
int build(int p, int l, int r, int x) {
if (l == r)
return num[p][x] = l;
num[p][x] = ++tot;
int mid = (l + r) / 2;
int ls = build(p * 2, l, mid, x);
int rs = build(p * 2 + 1, mid + 1, r, x);
if (x == 0)
{
v[num[p][x]].push_back(mp(ls, 0));
v[num[p][x]].push_back(mp(rs, 0));
}
else
{
v[ls].push_back(mp(num[p][x], 0));
v[rs].push_back(mp(num[p][x], 0));
}
return num[p][x];
}
void update(int p, int l, int r, int a, int b, int x, int y, int op) {
if ((l == a) && (r == b))
{
if (op == 0)
v[x].push_back(mp(num[p][op], y));
else
v[num[p][op]].push_back(mp(x, y));
return ;
}
int mid = (l + r) / 2;
if (b <= mid)
update(p * 2, l, mid, a, b, x, y, op);
else if (a > mid)
update(p * 2 + 1, mid + 1, r, a, b, x, y, op);
else
{
update(p * 2, l, mid, a, mid, x, y, op);
update(p * 2 + 1, mid + 1, r, mid + 1, b, x, y, op);
}
}
int main() {
memset(dist, -1, sizeof(dist));
read(n); read(m); read(st);
tot = n;
build(1, 1, n, 0);
build(1, 1, n, 1);
for (int i = 1; i <= m; i++)
{
int t;
read(t);
if (t == 1)
{
int a, b, c;
read(a); read(b); read(c);
v[a].pb(make_pair(b, c));
}
else
{
int v, l, r, w;
scanf("%d%d%d%d", &v, &l, &r, &w);
update(1, 1, n, l, r, v, w, t - 2);
}
}
s.insert(mp(0, st));
while (!s.empty())
{
ll y = s.begin()->st, x = s.begin()->ed;
s.erase(s.begin());
if (dist[x] != -1)
continue;
dist[x] = y;
for (int i = 0; i < v[x].size(); i++)
if (dist[v[x][i].st] == -1)
s.insert(mp(1ll * v[x][i].ed + dist[x], v[x][i].st));
}
for (int i = 1; i <= n; i++)
printf("%I64d ", dist[i]);
return 0;
}