P7480 Reboot from Blue 线段树优化建图跑最短路

P7480 Reboot from Blue - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

建图,跑最短路。

直接建图太大,其中很多路都是无用的。考虑优化,用数据结构找到一点前后第一个比该点油价低的即可。我用线段树来找这两个位置。

需要注意:

  • dis[]值的初始化,0x3f3f3f3f不行(
  • 边数要开大点,大概是 3 × N 3 \times N 3×N条边
  • 注意判断终点是否为加油站((

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 21; // 注意:N要开大点
#define int long long
struct no {
    int c,x;
    bool operator<(const no& rhs) const {
        return x < rhs.x;
    }
}a[N];

// 基础线段树 - 最值
struct tree {
    int l, r, mi;
}tr[N << 2];
int inline ls(int x) {return x << 1; }
int inline rs(int x) {return x << 1 | 1; }
void pushup(int u) {
    tr[u].mi = min(tr[ls(u)].mi, tr[rs(u)].mi);
}
void build(int u, int l, int r) {
    if(l == r) tr[u] = {l, r, a[l].c};
    else {
        tr[u] = {l, r, (int)3e18};
        int mid = l + r >> 1;
        build(ls(u), l, mid);
        build(rs(u), mid + 1, r);
        pushup(u);
    }
}

// 查找左边第一个小于val的位置
int qryl(int u, int l, int r, int val) {
    if(tr[u].l == tr[u].r) {
        return tr[u].mi < val ? tr[u].l : -1;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if(tr[u].l >= l && tr[u].r <= r) {
        // 先搜索右边的,保证最近
        if(tr[rs(u)].mi < val) return qryl(rs(u), l, r, val);
        // 再搜索左边的
        if(tr[ls(u)].mi < val) return qryl(ls(u), l, r, val);
        
        // 都没有找到,返回-1.左边不会有满足条件的了
        return -1;
    }

    // 这两段是是找区间(ql, qr),保证 (ql, qr) 和(l, r) 有交集的
    if(r <= mid) return qryl(ls(u), l, r, val);
    if(l > mid) return qryl(rs(u), l, r, val);

    // 有交集的情况下,先搜索右边的,保证最近
    int res = qryl(rs(u), l, r, val);
    // 不为-1,说明找到了,直接返回
    if(res != -1) return res;
    // 没找到,再搜索左边的
    res = qryl(ls(u), l, r, val);
    return res;
}

// 查找右边第一个小于val的位置
    // 同上
int qryr(int u, int l, int r, int val) {
    if(tr[u].l == tr[u].r) {
        return tr[u].mi < val ? tr[u].l : -1;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if(tr[u].l >= l && tr[u].r <= r) {
        if(tr[ls(u)].mi < val) return qryr(ls(u), l, r, val);
        if(tr[rs(u)].mi < val) return qryr(rs(u), l, r, val);
        return -1;
    }
    if(mid < l) return qryr(rs(u), l, r, val);
    if(mid >= r) return qryr(ls(u), l, r, val);
    int res = qryr(ls(u), l, r, val);
    if(res != -1) return res;
    res = qryr(rs(u), l, r, val);
    return res;
}

// dijstra堆优化模板
    // 这里N要开大点,因为:前后建边、与终点建边
int e[N],h[N], ne[N], w[N], idx, dis[N], vis[N];
void add(int u, int v, int val) {
    e[idx] = v, w[idx] = val, ne[idx] = h[u], h[u] = idx++;
}
signed main()
{
    ios::sync_with_stdio(0);
    int n, st, ed; cin>>n>>st>>ed;
    bool fg = true; // 终点不是加油站
    for(int i = 1; i <= n; ++i) {
        cin>>a[i].c>>a[i].x;
        if(a[i].x == ed) fg = false;
    }
    // 终点不是加油站,加一个虚拟加油站
    if(fg) {
        a[++n] = {-1, ed};
    }
    // 按照坐标x排序
    sort(a + 1, a + n + 1);

    // 建立线段树
    build(1, 1, n);

    // 建立图
    memset(h, -1, sizeof(h));
    // memset(dis, 0x3f, sizeof(dis));
    for(int i = 0; i <= n; ++i) dis[i] = 1e18;
    int dijst = -1, dijed = -1; // 终点有一个测试里可能有多个,要找到最优的作为终点
    for(int i = 1; i <= n; ++i) {
        if(a[i].x == st) dijst = i;
        if(a[i].x == ed) dijed = i;

        int lv = qryl(1, 1, i, a[i].c);
        if(lv != -1) {
            add(i, lv, a[i].c * abs(a[i].x - a[lv].x));
        }
        int rv = qryr(1, i, n, a[i].c);
        if(rv != -1) {
            add(i, rv, a[i].c * abs(a[i].x - a[rv].x));
        }
    }

    // 终点不是加油站,加边
    for(int i = 1; i <= n; ++i) {
        if(i == dijed) continue;
        add(i, dijed, a[i].c * abs(a[i].x - a[dijed].x));
    }

    // 跑dijstra
    dis[dijst] = 0;
    priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> q;
    q.push({0, dijst});
    while(q.size()) {
        auto tmp = q.top(); q.pop();
        int u = tmp.second, d = tmp.first;
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i = h[u]; ~i; i = ne[i]) {
            int y = e[i];
            if(dis[y] > dis[u] + w[i]) {
                dis[y] = dis[u] + w[i];
                q.push({dis[y], y});
            }
        }
    }

    cout<<dis[dijed]<<'\n';
}
上一篇:邮箱的正则表达式


下一篇:算法:bfs(深度优先搜索)