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. 20135313_exp5

    课程:Java程序与设计     班级:1353 姓 名:吴子怡  学号:20135313 小组成员: 20135113肖昱 成绩:             指导教师:娄嘉鹏       实验日期:2 ...

  2. 【分层最短路】Joyride

    http://codeforces.com/gym/101873 C 多开一维状态记录时间,d[i][t] = 经过时间t走到节点i的最小花费 每一个状态分别向"原地等待"与&qu ...

  3. 团队Alpha冲刺(十)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  4. 【c】线性表

    数据对象集:线性表是N(>=0)个元素构成的有序序列,a1,a2,a3.....a(N-1),aN,a(N+1) 线性表上的基本操作有: ⑴ 线性表初始化:Init_List(L)初始条件:表L ...

  5. PHP中大括号用法

    Php中"{}"大括号的用法总结 在PHP中,大括号“{}”可以起到如下作用: 1.将多个独立语句合并为一个复合语句,例如 if ... else ...中经常如此使用 2.在变量 ...

  6. utuntu下安装pip&pip3

    在utuntu下建议不要使用apt-get install 安装pip,会出现很多问题. 建议使用如下方式安装: wget https://bootstrap.pypa.io/get-pip.py - ...

  7. SPOJ4717——Grid Points in a Triangle

    题目的意思很简单.就是要你求出斜率为a/b的一个点在原点,一条边为x=n的RT三角形里面有多少个整数点? 看完题目后依然没有思路,依然去看各个神牛写的题解.后来才反应过来. 题目的正解应该是这样的.递 ...

  8. bzoj1390 [CEOI2008] Fence

    题意 给出n个白点和m个黑点.现在你需要选择一些白点把黑点圈起来.每有一个黑点不能被选出的白点组成的凸包包含就需要付出111的代价,每选出一个白点就需要付出20的代价.要求最小化代价之和 n,m< ...

  9. 转---秒杀多线程第五篇 经典线程同步 关键段CS

    上一篇<秒杀多线程第四篇 一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题. 本文首先介绍下如何使用关键段,然 ...

  10. 【Mybatis】Mybatis的sql模糊查询

    这个网站中有很多方法.https://code.google.com/p/mybatis/issues/detail?id=85 自己试验了如下的方法. 1.  参数中直接加入%% param.set ...