Solution

一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门

然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$

然后拓扑排序。 由于一次引爆的炸弹 一定是一个连续的区间内, 所以只需要记录左右边界, 并将左右边界转移给能到达它的联通块。

没写手工栈一直RE的我心里$mmp$啊。 为什么网上的题解都不写手工栈$QAQ$

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
#define ll long long
using namespace std; const int N = ;
const int mod = 1e9 + ;
const ll inf = 3e18; int n, lscnt;
int head[N], tot;
int Head[N], Tot;
int low[N], dfn[N], dfstot;
int c[N], col;
ll minn[N], maxn[N], ls[N];
int st[N], tp;
bool vis[N], inq[N]; struct edge {
int nxt, to;
}e[N * ], E[N * ]; struct boom {
ll pos, r;
}bo[N]; ll read() {
ll X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} void add(int u, int v) {
e[++tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
} void Add(int u, int v) {
E[++Tot].to = v;
E[Tot].nxt = Head[u];
Head[u] = Tot;
} namespace SegT {
#define mid ((l + r) >> 1)
int lc[N], rc[N], root, cnt; void build(int &p, int l, int r) {
if (l == r) {
p = l; return;
}
p = ++cnt;
build(lc[p], l, mid);
build(rc[p], mid + , r);
add(p, lc[p]); add(p, rc[p]);
} void update(int L, int R, int c, int l, int r, int x) {
if (L > R) return;
if (L <= l && r <= R) {
add(c, x); return;
}
if (mid >= L)
update(L, R, c, l,mid, lc[x]);
if (mid < R)
update(L, R, c, mid + , r, rc[x]);
}
}using namespace SegT; #define R register
int stx[N], sti[N], lv, stnt[N];
#define u stx[lv]
#define nt stnt[lv]
#define i sti[lv]
void tarjan(int x) {
lv = ; stx[] = x;
start:;
low[u] = dfn[u] = ++dfstot;
inq[u] = true; st[++tp] = u;
for (i = head[u]; i; i = e[i].nxt) {
nt = e[i].to;
if (!dfn[nt]) {
// tarjan(nt);
stx[lv + ] = nt;
lv++;
goto start;
end:;
low[u] = min(low[u], low[nt]);
}
else if (inq[nt]) low[u] = min(low[u], dfn[nt]);
}
if (low[u] == dfn[u]) {
col++;
for (; tp;) {
int z = st[tp--];
inq[z] = false;
if (z <= n)
maxn[col] = max(maxn[col], bo[z].pos + bo[z].r),
minn[col] = min(minn[col], bo[z].pos - bo[z].r);
c[z] = col;
if (z == u) break;
}
}
--lv;
if (lv) goto end;
}
#undef u
#undef nt
#undef i void dfs(int u) {
vis[u] = true;
for (R int i = Head[u]; i; i = E[i].nxt) {
int nt = E[i].to;
if (vis[nt] == false) dfs(nt);
minn[u] = min(minn[u], minn[nt]);
maxn[u] = max(maxn[u], maxn[nt]);
}
} int fdl(ll x) {
return lower_bound(ls + , ls + + lscnt, x) - ls;
} int fdr(ll x) {
int re = lower_bound(ls + , ls + + lscnt, x) - ls;
if (ls[re] == x) return re;
else return re - ;
} int main()
{
cnt = n = rd;
for (R int i = ; i <= n; ++i) {
bo[i].pos = rd; bo[i].r = rd;
ls[++lscnt] = bo[i].pos;
}
sort(ls + , ls + + lscnt);
lscnt = unique(ls + , ls + + lscnt) - ls - ;
build(root, , n);
for (int i = ; i <= n; ++i) {
int l = fdl(bo[i].pos - bo[i].r), r = fdr(bo[i].pos + bo[i].r), m = fdl(bo[i].pos);
update(l, m - , m, , n, root);
update(m + , r, m, , n, root);
}
for (int i = ; i < N; ++i)
maxn[i] = -inf, minn[i] = inf;
for (int i = ; i <= cnt; ++i)
if (!dfn[i]) tarjan(i);
for (int u = ; u <= cnt; ++u)
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (c[u] == c[v])
continue;
Add(c[u], c[v]);
}
for (int i = ; i <= col; ++i) dfs(i);
ll ans = ;
for (int i = ; i <= n; ++i) {
int l = fdl(minn[c[i]]), r = fdr(maxn[c[i]]);
(ans += 1LL * i * (r - l + ) % mod) %= mod;
}
printf("%lld\n", ans);
}

BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan的更多相关文章

  1. bzoj5017 [Snoi2017]炸弹 (线段树优化建图+)tarjan 缩点+拓扑排序

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5017 题解 这个题目方法挺多的. 线段树优化建图 线段树优化建图的做法应该挺显然的,一个炸弹能 ...

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

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

  3. [SNOI2017]炸弹[线段树优化建图]

    [SNOI2017]炸弹 线段树优化建图,然后跑一边tarjan把点全部缩起来,炸一次肯定是有连锁反应的所以整个连通块都一样-于是就可以发现有些是只有单向边的不能忘记更新,没了. #include & ...

  4. bzoj5017 炸弹 (线段树优化建图+tarjan+拓扑序dp)

    直接建图边数太多,用线段树优化一下 然后缩点,记下来每个点里有多少个炸弹 然后按拓扑序反向dp一下就行了 #include<bits/stdc++.h> #define pa pair&l ...

  5. 『炸弹 线段树优化建图 Tarjan』

    炸弹(SNOI2017) Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸 时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi ...

  6. BZOJ5017 [Snoi2017]炸弹[线段树优化建边+scc缩点+DAG上DP/线性递推]

    方法一: 朴素思路:果断建图,每次二分出一个区间然后要向这个区间每个点连有向边,然后一个环的话是可以互相引爆的,缩点之后就是一个DAG,求每个点出发有多少可达点. 然后注意两个问题: 上述建边显然$n ...

  7. 炸弹:线段树优化建边+tarjan缩点+建反边+跑拓扑

    这道题我做了有半个月了...终于A了... 有图为证 一句话题解:二分LR线段树优化建边+tarjan缩点+建反边+跑拓扑统计答案 首先我们根据题意,判断出来要炸弹可以连着炸,就是这个炸弹能炸到的可以 ...

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

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

  9. BZOJ5017 炸弹(线段树优化建图+Tarjan+拓扑)

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

随机推荐

  1. oracle中如何修改用户名和密码

    1.以Windows操作系统为例,打开命令提示符,输入命令sqlplus /nolog ,进入oracle控制台,并输入 conn /as sysdba;以DBA角色进入. 2.连接成功后,输入“se ...

  2. 算法实践--最长递增子序列(Longest Increasing Subsquence)

    什么是最长递增子序列(Longest Increasing Subsquence) 对于一个序列{3, 2, 6, 4, 5, 1},它包含很多递增子序列{3, 6}, {2,6}, {2, 4, 5 ...

  3. Hadoop 管理工具HUE配置-Hive配置

    1 前言 首先要配置好Hive,可以参见:http://www.cnblogs.com/liuchangchun/p/4761730.html 2 hive配置 找到beeswax标签,不叫hive, ...

  4. note 1 对象和数据类型

    /#行注释 print "Hello World" 对象 五种基本类型 字符串 (string),简记为str 使用 ' ' 或 " " 括起来的一系列字符 整 ...

  5. WindowsServer2012R2开机进入CMD,关闭后黑屏问题

    原因分析: 因为自己在卸载IIS的时候,不小心卸载了.net framework,系统没有了图形界面(由完整模式Full变为了核心模式core),需要重新恢复.net framework4.5. 解决 ...

  6. ps-如何去背景色(将背景色变透明)

    由于生活或工作的需求,图片的处理是必不可少.其中将图片某一部分变为透明,或者截取图片的某一部分比较常见. 1.首先,打开待处理的图片: 2.复制背景图层,将背景图层设为不可见(左边的眼睛即可),选择左 ...

  7. js中的数值转换

    js中有3个函数可以把非数值转换为数值:Number().parseInt().parseFloat().其中Number()可以用于任何数据类型.parseInt()及parseFloat()用于将 ...

  8. TCP‘三次握手’和‘四次挥手’(通俗易懂)

      概述 我们都知道 TCP 是 可靠的数据传输协议,UDP是不可靠传输,那么TCP它是怎么保证可靠传输的呢?那我们就不得不提 TCP 的三次握手和四次挥手. 三次握手 下图为三次握手的流程图 下面通 ...

  9. python学习笔记_week26

    note 一.CMDB -采集资产 -API -后台管理 -资产列表(CURD) -业务线列表(CURD) -用户列表(CURD) -组列表(CURD) ... ===>简单<=== 公共 ...

  10. 工作流调度器azkaban

    为什么需要工作流调度系统 一个完整的数据分析系统通常都是由大量任务单元组成: shell脚本程序,java程序,mapreduce程序.hive脚本等 各任务单元之间存在时间先后及前后依赖关系 为了很 ...