CodeForces - 786B Legacy 【思维最短路 and 线段树优化建图】

传送门
题意: 有n个点, q次操作, 操作有三种:
1 u v w 表示u -> v 有有一条权值为w的边.
2 u l r w 表示 u 到 下标在[l, r] 的城市 有一条权值为w的边
3 u l r w 表示 下标在[l, r] 的城市 到 u 有一条权值为w的边
然后求给出起点s, 输出s到每一个点的最小花费. 不能到达输出-1.

思路: 很明显直接暴力建边, 建的过程就会T, 边数也非常多. 但是我们可以发现连的城市是一段连续区间, 所以我们需要利用这个特性, 那么就很明显想到线段树区间分层问题. 这样我们的建图的边数最多只有nlogn条边, 建边的过程也优化了, 具体操作过程为: 我们需要两颗线段树. 第一颗叫入树, 第二颗叫出树, 入树的每段区间都向左右儿子连一条权值为0的边, 叶子节点向相应点连一条权值为0的边, 然后出树相反, 是左右儿子向父节点连一条权值为0的边, 相应点向出树的叶子节点连一条权值为0的边. 都是有向边.
然后对于操作, 1操作直接连点加权边, 2操作u 向 入树对应区间的所有标号连一条权值为w的有向边, 3操作 出树的所有区间标号向u连一条权值为w的有向边. 这样就优化完成了, 并且符合要求, 画出来就很明显可以看出来了….

那么点边这么多, 肯定用优化的dij跑最短路呀. 注意点数和边数, 尽量大一点.

AC Code

const int maxn = 6e5 + 5;
const int maxm = 3e6 + 5;
struct node {
    int to, next; ll w;
    bool operator < (const node &_) const {
        return w > _.w;
    }
}e[maxm];
int n, q, s, idx;
int head[maxn], cnt;
struct Tree {
    int tl, tr, val;
}tree[2][maxn];
void init() {
    Fill(head, -1); cnt = 0;
    Fill(tree, 0); idx = n;
}
void add(int u, int v, int w) {
    e[cnt] = node{v, head[u], w};
    head[u] = cnt++;
}
void build(int id, int l, int r, int f) {
    tree[f][id].val = ++idx;
    tree[f][id].tl = l, tree[f][id].tr = r;
    if (l == r) {
        if (!f) add(idx, l, 0);
        else add(l, idx, 0);
        return ;
    }
    int mid = (l + r) >> 1;
    build(id<<1, l, mid, f);
    build(id<<1|1, mid+1, r, f);
    if (!f) {
        add(tree[f][id].val, tree[f][id<<1].val, 0);
        add(tree[f][id].val, tree[f][id<<1|1].val, 0);
    }
    else {
        add(tree[f][id<<1].val, tree[f][id].val, 0);
        add(tree[f][id<<1|1].val, tree[f][id].val, 0);
    }
}
vector<int>vs;
void query(int id, int ql, int qr, int f) {
    int l = tree[f][id].tl, r = tree[f][id].tr;
    if (ql <= l && r <= qr) {
        vs.pb(tree[f][id].val);
        return ;
    }
    int mid = (l + r) >> 1;
    if (qr <= mid) query(id<<1, ql, qr,f );
    else if (ql > mid) query(id<<1|1, ql, qr, f);
    else {
        query(id<<1, ql, mid, f);
        query(id<<1|1, mid+1, qr, f);
    }
}
ll dis[maxn]; bool vis[maxn];
void dij(int st,int ed) {
    priority_queue<node> q;
    for (int i = 1 ; i <= maxn-5 ; i ++) {
        dis[i] = INF;
        vis[i] = 0;
    }
    dis[st] = 0;
    q.push(node{st, 0, 0});
    while (!q.empty()) {
        node u = q.top();
        q.pop();
        if(vis[u.to]) continue;
        vis[u.to] = 1;  

        for (int i = head[u.to]; ~i; i = e[i].next) {
            int to = e[i].to;
            if (dis[to] > dis[u.to] + e[i].w) {
                dis[to] = dis[u.to] + e[i].w;
                q.push(node{to, 0, dis[to]});
            }
        }
    }
}
void solve() {
    while(~scanf("%d%d%d", &n, &q, &s)) {
        init();
        build(1, 1, n, 0);
        build(1, 1, n, 1);
        while(q--) {
            int op, u, v, l, r, w;
            scanf("%d%d", &op, &u);
            if (op == 1) {
                scanf("%d%d", &v, &w);
                add(u, v, w);
            }
            else if (op == 2) {
                scanf("%d%d%d", &l, &r, &w);
                vs.clear();
                query(1, l, r, 0);
                for (int i = 0 ; i < sz(vs) ; i ++) {
                    add(u, vs[i], w);
                }
            }
            else {
                scanf("%d%d%d", &l, &r, &w);
                vs.clear();
                query(1, l, r, 1);
                for (int i = 0 ; i < sz(vs) ; i ++) {
                    add(vs[i], u, w);
                }
            }
        }
        dij(s, n);
        for (int i = 1 ; i <= n ; i ++) {
            printf("%lld%c", dis[i] == INF ? -1ll : dis[i], i == n ?'\n':' ');
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值