区间->点,点->区间,线段树优化建图+dijstra Codeforces Round #406 (Div. 2) D
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的更多相关文章
- BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...
- 【ARC069F】Flags 2-sat+线段树优化建图+二分
Description 数轴上有 n 个旗子,第 ii 个可以插在坐标 xi或者 yi,最大化两两旗子之间的最小距离. Input 第一行一个整数 N. 接下来 N 行每行两个整数 xi, ...
- 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆. 现在 ...
- 【bzoj4699】树上的最短路(树剖+线段树优化建图)
题意 给你一棵 $n$ 个点 $n-1$ 条边的树,每条边有一个通过时间.此外有 $m$ 个传送条件 $(x_1,y_1,x_2,y_2,c)$,表示从 $x_1$ 到 $x_2$ 的简单路径上的点可 ...
- 【bzoj3073】[Pa2011]Journeys 线段树优化建图+堆优化Dijkstra
题目描述 Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a, ...
- 【bzoj4383】[POI2015]Pustynia 线段树优化建图+差分约束系统+拓扑排序
题目描述 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r- ...
- 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 ...
- BZOJ_4383_[POI2015]Pustynia_线段树优化建图+拓扑排序
BZOJ_4383_[POI2015]Pustynia_线段树优化建图+拓扑排序 Description 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息 ...
- 【2019.7.26 NOIP模拟赛 T3】化学反应(reaction)(线段树优化建图+Tarjan缩点+拓扑排序)
题意转化 考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数. 于是,这个问题就被我们搬到了图上,成了一个图论题. 优化建图 考虑我们每次需要将一个区间向一个区间连边. ...
随机推荐
- Beta版冲刺前准备
[团队概要] 团队项目名:小葵日记 团队名:日不落战队 队员及角色: 队员 角色 备注 安琪 前端工程师 队长 佳莹 前端工程师 智慧 后端工程师 章鹏 后端工程师 语恳 UI设计师 炜坤 前端工程师 ...
- java分页算法
int totalPageNum = (totalRecord + pageSize - 1) / pageSize;
- Snapseed玩出新高度,分分钟让你成p图大神! 转
(,,・∀・)ノ゛嗨呀 小阔爱们! 不知道大家记不记得~ 上周我们的副条发了一篇: <看过他的照片,我才知道什么是创意摄影> 德国仅22岁超现实主义艺术家Justin Peters 创造了 ...
- Laravel中如何添加新字段,如何指定在某个字段后而不是添加在最后
解答:
- java 基础 --File
1, 创建文件 File file = new File(path); file.createNewFile(); //如果路径不存在,会抛异常 file.mkdir();//如果路径不存在,返回fa ...
- java 父类的引用调用自己的属性 但是调用的方法必须是重写过的父类的方法 因为编译时候把他当作父类 运行时候才是他自己 所以必须重写父类得方法
- BZOJ 1066:[SCOI2007]蜥蜴(最大流)
蜥蜴Description在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外. 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到 ...
- 【uoj#48】[UR #3]核聚变反应强度 数论
题目描述 给出一个长度为 $n$ 的数列 $a$ ,求 $a_1$ 分别与 $a_1...a_n$ 的次大公约数.不存在则输出-1. 输入 第一行一个正整数 $n$ . 第二行 $n$ 个用空格隔开的 ...
- MySQL复制 -- Binlog (1)
复制之所以工作得益于MySQL把对数据库的变更都记录在 binlog中,然后主库把它读出来,放到从库上去应用.当然binlog 的用途不仅限于此,比如 PITR等 在5.1.4版本以前,binlog格 ...
- Tomcat+JDK安装和配置
Tomcat+JDK安装和配置 一.打开FlashFXP软件,建立连接,选择需要的包,右击传输到 /home/guest中 二.进入到:cd /home/guest中,对tomcat包进行解压 三.将 ...