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. "Hello World!"团队第十次会议

    Scrum会议 今天是我们"Hello World!"团队第十次召开会议,博客内容是: 1.会议时间 2.会议成员 3.会议地点 4.会议内容 5.todo list 6.会议照片 ...

  2. 周总结<2>

    本打算在这周日写周总结的,但由于事情太多,还要组织团日活动,所以没时间写.不过上周主要是一些书本上的学习,但是在周日的时候完成了一款小游戏还是比较有成就感的,但是主要是因为html的考试才去做的. 代 ...

  3. lintcode-107-单词切分

    107-单词切分 给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词. 样例 给出 s = "lintcode" dict = [" ...

  4. lintcode-391-数飞机

    391-数飞机 给出飞机的起飞和降落时间的列表,用 interval 序列表示. 请计算出天上同时最多有多少架飞机? 注意事项 如果多架飞机降落和起飞在同一时刻,我们认为降落有优先权. 样例 对于每架 ...

  5. .NET Core 控制台中文乱码问题!

    class Program { static void Main(string[] args) { Encoding.RegisterProvider(CodePagesEncodingProvide ...

  6. PAT 甲级 1059 Prime Factors

    https://pintia.cn/problem-sets/994805342720868352/problems/994805415005503488 Given any positive int ...

  7. jQuery的滚动监听

    jQuery的滚动监听 1.当前滚动的地方的窗口顶端到整个页面顶端的距离: var winPos = $(window).scrollTop(); 2.获取指定元素的页面位置: $(val).offs ...

  8. 安装php先行库

    libmcrypt libconv mhash  ./configure --prefix=/usr/local mcrypt 安装完成后在当前目录还要 /sbin/ldconfig ./config ...

  9. 判断一个变量是不是json,以及如何将变量转换成json

    https://blog.csdn.net/A123638/article/details/52486975这里看到一个很好的方法 // 判断变量是不是jsonisJson(variable: any ...

  10. Linux下修改环境变量PATH

    1.什么是环境变量(PATH) 在Linux中,在执行命令时,系统会按照PATH的设置,去每个PATH定义的路径下搜索执行文件,先搜索到的文件先执行. 我们知道查阅文件属性的指令ls 完整文件名为:/ ...