codeforces786B - Legacy

20 篇文章 0 订阅
4 篇文章 0 订阅

题面在这里

题意:

三种操作:
1.给a->b加一条边
2.给a->[b,c]分别加一条权值相同的边
3.给[a,b]->c分别加一条权值相同的边
然后问一个点到其他点的最短路

做法:

考虑在线段树上加边
一个点对应一个区间,就把那个点和那个区间所对应的线段树节点编号加边
然后线段树上父亲和儿子再分别连一条权值为0的边
然后就可以直接跑最短路了
好妙啊。

代码:

/*************************************************************
    Problem: codeforces 786B - Legacy
    User: bestFy
    Language: C++
    Result: Accepted
    Time: 358 ms
    Memory: 135528 KB
    Submit_Time: 2018-01-03 15:01:06
*************************************************************/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<vector>
#include<queue>
#define PII pair<int, int>
#define PLI pair<long long, int>
#define mp make_pair
#define pb push_back
#define mid (l+r>>1)
#define lc o<<1
#define rc o<<1|1
#define F first
#define S second
using namespace std;
typedef long long LL;

inline LL read()
{
    char ch = getchar(); LL x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10 + ch-'0';
    return x*op;
}
inline void writeabs(LL a) { if(a >= 10) writeabs(a/10); putchar(a%10+'0'); }
inline void write(LL a) { if(a < 0) putchar('-'); writeabs(abs(a)); }

const int N = 2000010;
int n, m, start, tot;
int ver[2][N<<2];
LL dis[N];
vector<PII> v[N];

inline int build(int o, int l, int r, int d)
{
    if(l == r) return ver[d][o] = l;
    ver[d][o] = ++ tot;
    int cl = build(lc, l, mid, d), cr = build(rc, mid+1, r, d);
    if(!d) { v[ver[d][o]].pb(mp(cl, 0)); v[ver[d][o]].pb(mp(cr, 0)); }
    else { v[cl].pb(mp(ver[d][o], 0)); v[cr].pb(mp(ver[d][o], 0)); }
    return ver[d][o];
}
inline void update(int o, int l, int r, int x, int y, int z, int w, int d)//[x,y] and z
{
    if(l == x && r == y) {
        if(!d) v[z].pb(mp(ver[d][o], w)); else v[ver[d][o]].pb(mp(z, w));
        return;
    }
    if(y <= mid) update(lc, l, mid, x, y, z, w, d);
    else if(x > mid) update(rc, mid+1, r, x, y, z, w, d);
    else update(lc, l, mid, x, mid, z, w, d), update(rc, mid+1, r, mid+1, y, z, w, d);
}
int main()
{
    n = read(), m = read(), start = read(); tot = n;
    build(1, 1, n, 0); build(1, 1, n, 1);
    for(int i = 1; i <= m; i ++) {
        int opt = read(), a = read(), b = read(), c = read(), w;
        if(opt == 1) v[a].pb(mp(b, c));
        else { w = read(); update(1, 1, n, b, c, a, w, opt-2); }
    }
    memset(dis, -1, sizeof dis); dis[start] = 0;
    priority_queue<PLI> q; q.push(mp(0, start));
    while(!q.empty()) {
        int u = q.top().S; q.pop();
        for(int i = 0; i < v[u].size(); i ++) {
            int nw = v[u][i].F; LL cost = v[u][i].S;
            if(dis[nw] == -1 || dis[nw] > dis[u]+cost) {
                dis[nw] = dis[u]+cost;
                q.push(mp(-dis[nw], nw));
            }
        }
    }
    for(int i = 1; i <= n; i ++) cout << dis[i] << ' ';
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值