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;
}
这题可以考虑线段树建图
这题思维中为了保持区间连通性建长度为
0
0
0 的边也和线段树建图的一些地方类似