CodeForces - 786B (线段树?优化建图最短路)

题目链接
题解:
这道题太神仙了!
由于有整个区间指向点的边和点指向一整个区间的边,所以边数它爆炸了。
那么怎么样可以将边数减下来呢?这里以一个点连向一个区间的点为例子,我们可以建立一颗线段树,每个点代表一个区间,每个区间连一条边到它的各个子区间,边权为 0 ,然后将叶子节点连向各个原始的节点。那么要增加一个点连向一个区间的边就变成了这个点连向这个区间所包含的线段树中的节点即可。
同样,增加一个区间到一个点的边我们可以再建一棵线段树,只是把原来的边反过来即可。(注意不可以建在一起,因为双向边都是 0 的话最短路就一直是 0 了!)
下面这张图是第二个样例建的图:
图片
代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
#define P pair<ll, int>
#define debug(x) cout << #x << ": " << x << endl
#define fastio ios::sync_with_stdio(false), cin.tie(0)
const int mod = 1e9 + 7;
const int M = 1000000 + 10;
const int N = 500000 + 10;

int n, q, s, t;
vector<P> G[N];
ll d[N];
int id[2][N], cnt;

void dijkstra(int st)
{
    for(int i = 1; i <= cnt; i ++) d[i] = INF; d[st] = 0;
    bool vis[N] = { false };
    priority_queue<P, vector<P>, greater<P>> que;
    que.push(P{0, st});
    while(que.size()) {
        int now = que.top().second; ll dis = que.top().first;
        que.pop();
        if(vis[now]) continue;
        vis[now] = true;
        for(int i = 0; i < G[now].size(); i ++) {
            if(dis + G[now][i].first < d[G[now][i].second]) {
                d[G[now][i].second] = dis + G[now][i].first;
                que.push(P{d[G[now][i].second], G[now][i].second});
            }
        }
    }
}

void build(int kd, int &now, int l, int r, int rt)
{
    now = ++cnt;
    if(l == r) {
        if(kd) G[now].push_back(P{0, l});
        else G[l].push_back(P{0, now});
        return ;
    }
    int mid = (l + r) >> 1;
    build(kd, id[kd][rt << 1], l, mid, rt << 1);
    build(kd, id[kd][rt << 1 | 1], mid + 1, r, rt << 1 | 1);
    if(kd) {
        G[now].push_back(P{0, id[kd][rt << 1]});
        G[now].push_back(P{0, id[kd][rt << 1 | 1]});
    } else {
        G[id[kd][rt << 1]].push_back(P{0, now});
        G[id[kd][rt << 1 | 1]].push_back(P{0, now});
    }
}

void single(int st, int L, int R, int val, int l, int r, int rt)
{
    if(L <= l && r <= R) {
        G[st].push_back(P{val, id[1][rt]});
        return ;
    }
    int mid = (l + r) >> 1;
    if(L <= mid) single(st, L, R, val, l, mid, rt << 1);
    if(mid < R) single(st, L, R, val, mid + 1, r, rt << 1 | 1);
}

void segment(int ed, int L, int R, int val, int l, int r, int rt)
{
    if(L <= l && r <= R) {
        G[id[0][rt]].push_back(P{val, ed});
        return ;
    }
    int mid = (l + r) >> 1;
    if(L <= mid) segment(ed, L, R, val, l, mid, rt << 1);
    if(mid < R) segment(ed, L, R, val, mid + 1, r, rt << 1 | 1);
}

signed main()
{
    scanf("%d %d %d", &n, &q, &s);
    cnt = n;
    build(0, id[0][1], 1, n, 1);
    build(1, id[1][1], 1, n, 1);
    for(int i = 1, t; i <= q; i ++) {
        scanf("%d", &t);
        if(t == 1) {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            G[u].push_back(P{w, v});
        } else if(t == 2) {
            int v, l, r, w;
            scanf("%d %d %d %d", &v, &l, &r, &w);
            single(v, l, r, w, 1, n, 1);
        } else {
            int v, l, r, w;
            scanf("%d %d %d %d", &v, &l, &r, &w);
            segment(v, l, r, w, 1, n, 1);
        }
    }

    dijkstra(s);

    for(int i = 1; i <= n; i ++) {
        if(d[i] == INF) d[i] = -1;
        printf("%lld%c", d[i], i == n ? '\n' : ' ');
    }

    return 0;
}

/*

  Rejoicing in hope, patient in tribulation.

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值