区间->点,点->区间,线段树优化建图+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缩点+拓扑排序)
题意转化 考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数. 于是,这个问题就被我们搬到了图上,成了一个图论题. 优化建图 考虑我们每次需要将一个区间向一个区间连边. ...
随机推荐
- 【Alpha】阶段第三次Scrum Meeting
[Alpha]阶段第三次Scrum Meeting 工作情况 团队成员 今日已完成任务 明日待完成任务 刘峻辰 更新评论接口 获取课程评论接口 赵智源 编写脚本实现持续集成 整合前端进行部署 肖萌威 ...
- 【leetcode】215. Kth Largest Element in an Array
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...
- 面试- 阿里-. 大数据题目- 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
假如每个url大小为10bytes,那么可以估计每个文件的大小为50G×64=320G,远远大于内存限制的4G,所以不可能将其完全加载到内存中处理,可以采用分治的思想来解决. Step1:遍历文件a, ...
- C# 模拟串口发送接收
一.准备虚拟串口驱动工具 创建俩个虚拟串口,如图: 二.创建两个控制台程序 模拟串口的发送接收数据 1. 接收数据,代码如下: //遍历串行端口名称数组 foreach (string port in ...
- CSS自适应导航菜单
以下是一个简单实例,可以通过学习了解响应工菜单的制作. html <nav class="nav"> <ul> <li class="cur ...
- 使用cookie保存用户登录信息
写入Cookie HttpCookie _cookie = new HttpCookie("User"); _cookie.Values.Add("UserName&qu ...
- codeforces 1023 D. Array Restoration 并查集
D. Array Restoration time limit per test 1 second memory limit per test 256 megabytes input standard ...
- 解决二维数组转为ArrayList集合问题
1.修改前代码块 String[][] str = { { "语文", "100" }, { "英语", "90" }, ...
- Contest 2
A:辣鸡题.搜索怎么这么难啊.不会啊. B:裸的高斯消元,看起来可以优化到n2. #include<iostream> #include<cstdio> #include< ...
- Expect the Expected UVA - 11427(概率dp)
题意: 每天晚上你都玩纸牌,如果第一次就赢了,就高高兴兴的去睡觉,如果输了就继续玩.假如每盘游戏你获胜的概率都为p,每盘游戏输赢独立.如果当晚你获胜的局数的比例严格大于p时才停止,而且每天晚上最多只能 ...