http://codeforces.com/contest/787/problem/D

题目大意:有n个点,三种有向边,这三种有向边一共加在一起有m个,然后起点是s,问,从s到所有点的最短路是多少?

第一种边:u->v w 表示节点u到v有连接一条有向边,权值为w

第二种边:u->[l,r] w  表示节点u到区间[l,r]连接一条有向边,权值为w

第三种边:[l,r]->u w  表示区间[l, r]所有的节点到u都有一条有向边,权值为w

思路:

我们知道,对于dijstra都是用priority_queue来优化的,这样才保证了每个节点值访问一次的复杂度。

算法一:

对于第二种边和第三种边分别对[l,r]->u的这些点进行建图,再跑dij。但是最坏情况的建图是O(n*n),所以肯定会TLE

从算法一中看出,这道题的瓶颈在于如何处理区间和点之间的边的关系。

我们仔细的分析一下dijstra以后发现,每个节点的访问次数只有1,也就是说,优先队列里面取出来的点,取出来以后就不可能有比他更小的点了,而且这个点只会访问一次。

对于第一种边,u->v,那么很明显就是d[v] = d[u] +w,这个符合线段树中的单点更新

对于第二种边u->[l,r],那么也就是说,从u出发,到区间[l,r]所有的节点的val都修改为d[u] + w,这个符合线段树中的成段更新

对于第三种边[l,r]->u来说

从[l,r]出发->u的路径可以有无数条,那么如何做才能保证d[u]是从区间[l,r]里面得来的最小的呢?

根据priority_queue,那么我们最先访问过的点,就是必然是权值最小的,那么也就是说,假设当前我们访问到节点x,那么x属于区间[l,r]中,那么x一定是区间[l,r]中权值最小的,所以我们只需要利用这个d[x]去更新即可,即d[u] = d[x] + w。当然,如果这个[l,r]区间使用过了,就不需要再使用了,pop掉即可,因为后续再也用不到这个区间了!

所以这个也是线段树中的单点更新

第一次知道vector可以pop_ back

que就是priority_queue,tree保存目前节点能往右延伸到的最大值。

然后对一个值从队列里面访问过了,那么就不需要再次访问了

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1e5 + ;
const LL inf = 1e16;
int n, q, s;
vector<pair<int, LL> > one[maxn];
vector<pair<pair<int, int>, LL> > er[maxn], san[maxn];
///tree保存目前节点能往右边延伸到的最右端,que保存目前节点的最小值
int tree[maxn << ];
pair<LL, int> que[maxn << ];
LL lazy[maxn << ];
LL d[maxn]; inline void update_queue(int o, LL val){
if (que[o].fi == inf + ) return;
que[o].fi = min(que[o].fi, val);
if (lazy[o] == -) lazy[o] = val;
lazy[o] = min(lazy[o], val);
} void push_down(int o){
int lb = o << , rb = o << | ;
if (lazy[o] != -){
update_queue(lb, lazy[o]);
update_queue(rb, lazy[o]);
lazy[o] = -;
}
}
/*
因为对于第二种情况:v->[l,r]来说,[l,r]的值是相同的,所以我们就假定按照优先级来计算即可
对于第三种情况:[l, r]->v来说,[l,r]中只能是最小的那个点到v,因为只有这样才能保证最短
*/
void update(int ql, int qr, int l, int r, int o, LL val){
//printf("ql = %d qr = %d l = %d r = %d\n", ql, qr, l, r);
if (ql <= l && qr >= r){
if (val == inf + ) que[o] = mk(val, l);
else update_queue(o, val);
return ;
}
push_down(o);
int mid = (l + r) / ;
if (ql <= mid) update(ql, qr, l, mid, o << , val);
if (qr > mid) update(ql, qr, mid + , r, o << | , val);
que[o] = min(que[o << ], que[o << | ]);
// printf("que[%d] = %lld %d val = %lld\n", o, que[o], val);
} vector<pair<int, LL> > ve;
void find_point(int x, int l, int r, int o){
if (l > x || tree[o] < x) return ;
if (l == r){
while (!san[l].empty() && san[l].back().fi.fi >= x){
ve.push_back(mk(san[l].back().fi.se, san[l].back().se));
san[l].pop_back();
}
tree[o] = -;
if (!san[l].empty()) tree[o] = san[l].back().fi.fi;
return ;
}
int mid = (l + r) / ;
find_point(x, l, mid, o << );
find_point(x, mid + , r, o << | );
tree[o] = max(tree[o << ], tree[o << | ]);
} void solve(){
for (int i = ; i <= n; i++) d[i] = inf;
update(s, s, , n, , );///更新初始点
while (true){
int u = que[].se;
LL cost = que[].fi;
if (cost == inf + ) break;
d[u] = min(cost, d[u]);
update(u, u, , n, , inf + );///如果从队列里面访问过了,就不需要再访问了
///第一种,单点更新u->v
for (int i = ; i < one[u].size(); i++){
int v = one[u][i].fi; LL w = one[u][i].se;
update(v, v, , n, , d[u] + w);
}
///第二种,成段更新,u->[l,r]
for (int i = ; i < er[u].size(); i++){
int ql = er[u][i].fi.fi, qr = er[u][i].fi.se; LL w = er[u][i].se;
update(ql, qr, , n, , d[u] + w);
}
///第三种,单点更新,[l, r]->v,其中[l,r]包含了u节点
ve.clear();
find_point(u, , n, );
for (int i = ; i < ve.size(); i++){
int v = ve[i].fi; LL w = ve[i].se;
update(v, v, , n, , d[u] + w);
}
}
for (int i = ; i <= n; i++){
if (d[i] == inf) printf("-1 ");
else printf("%lld ", d[i]);
}
cout << endl;
} void build(int l, int r, int o){
if (l == r){
que[o] = mk(inf, l);
tree[o] = -;
if (!san[l].empty()) tree[o] = san[l].back().fi.fi;
return ;
}
int mid = (l + r) / ;
if (l <= mid) build(l, mid, o << );
if (r > mid) build(mid + , r, o << | );
tree[o] = max(tree[o << ], tree[o << | ]);
que[o] = min(que[o << ], que[o << | ]);
} int main(){
cin >> n >> q >> s;
for (int i = ; i <= q; i++){
int ty; scanf("%d", &ty);
if (ty == ){
int u, v; LL val; scanf("%d%d%lld", &u, &v, &val);
one[u].pb(mk(v, val));
}
else {
int u, l, r; LL val; scanf("%d%d%d%lld", &u, &l, &r, &val);
if (ty == ) er[u].pb(mk(mk(l, r), val));
else san[l].pb(mk(mk(r, u), val));
}
}
for (int i = ; i <= n; i++) sort(ALL(san[i]));
memset(lazy, -, sizeof(lazy));
build(, n, );
solve();
return ;
}

区间->点,点->区间,线段树优化建图+dijstra Codeforces Round #406 (Div. 2) D的更多相关文章

  1. BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan

    Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...

  2. 【ARC069F】Flags 2-sat+线段树优化建图+二分

    Description ​ 数轴上有 n 个旗子,第 ii 个可以插在坐标 xi或者 yi,最大化两两旗子之间的最小距离. Input ​ 第一行一个整数 N. ​ 接下来 N 行每行两个整数 xi, ...

  3. 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序

    题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足:  Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆.  现在 ...

  4. 【bzoj4699】树上的最短路(树剖+线段树优化建图)

    题意 给你一棵 $n$ 个点 $n-1$ 条边的树,每条边有一个通过时间.此外有 $m$ 个传送条件 $(x_1,y_1,x_2,y_2,c)$,表示从 $x_1$ 到 $x_2$ 的简单路径上的点可 ...

  5. 【bzoj3073】[Pa2011]Journeys 线段树优化建图+堆优化Dijkstra

    题目描述 Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a, ...

  6. 【bzoj4383】[POI2015]Pustynia 线段树优化建图+差分约束系统+拓扑排序

    题目描述 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r- ...

  7. BZOJ_4276_[ONTAK2015]Bajtman i Okrągły Robin_线段树优化建图+最大费用最大流

    BZOJ_4276_[ONTAK2015]Bajtman i Okrągły Robin_线段树优化建图+最大费用最大流 Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1 ...

  8. BZOJ_4383_[POI2015]Pustynia_线段树优化建图+拓扑排序

    BZOJ_4383_[POI2015]Pustynia_线段树优化建图+拓扑排序 Description 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息 ...

  9. 【2019.7.26 NOIP模拟赛 T3】化学反应(reaction)(线段树优化建图+Tarjan缩点+拓扑排序)

    题意转化 考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数. 于是,这个问题就被我们搬到了图上,成了一个图论题. 优化建图 考虑我们每次需要将一个区间向一个区间连边. ...

随机推荐

  1. 第一个scrum会议

    第一阶段冲刺任务认领: PM薛哥: 让手电筒亮起来 梁哥: 代码测试 康哥: 用户反馈等等

  2. Eclipse的黑色主题背景(github)

    MoonRise UI Theme   An early version of a dark UI theme for Eclipse 4+. Requirements Eclipse 4.2+ In ...

  3. CA如何吊销签署过的证书

    1: 客户端获取要吊销证书的serial(在使用证书上的主机执行) openssl x509 -in httpd.crt -noout -serial -subject   2:拿到证书的编号后,通过 ...

  4. 复利计算程序单元测试(C语言)

    对我们和复利计算程序,写单元测试. 有哪些场景? 期待的返回值 写测试程序. 运行测试. 我的复利计算程序是用C语言写的,不懂使用C语言的测试工具,所以用C语言的运行结果来反映测试结果. 测试模块(场 ...

  5. (七)对Jmeter进行参数化的俩种方式

    一.使用CSV Data Set Config: 1.添加CSV Data Set Config: 2.配置参数: Filename:文件名,指保存参数化数据的文件目录,可以相对或者绝对路径. Fil ...

  6. 虚拟机中安装 centOS,本地安装 SSH 连接 - 02

    先进入 centOS 中,查询虚拟机的 IP 地址: 双击打开 SSH 可视化客户端: 点击 Connect 需要输入之前那个[无论如何都要使用]的密码. 密码在[centOS - 01]里面设置过, ...

  7. javascript+html5+css3下拉刷新 数据效果

    文章摘自:suchso.com/projecteactual/javascript-html5-css3-taobao-xiala-data.html segmentfault.com/a/11900 ...

  8. 第78天:jQuery事件总结(一)

    jQuery事件总结(一)    现在就一点一点积累自己的知识体系,记录自己学到的和自己所理解的jQuery. JavaScript和HTML之间的交互式通过用户和浏览器操作页面时引发的事件机制来处理 ...

  9. 转---Android Audio System 之一:AudioTrack如何与AudioFlinger交换音频数据

    引子 Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFli ...

  10. Luogu4927 梦美与线段树(线段树+概率期望)

    每个节点被经过的概率即为该区间和/总区间和.那么所需要计算的东西就是每个节点的平方和了.修改对于某个节点的影响是使其增加2sum·l·x+l2x2.那么考虑对子树的影响,其中Σl2是定值,修改后Σsu ...