BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
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的更多相关文章
- bzoj5017 [Snoi2017]炸弹 (线段树优化建图+)tarjan 缩点+拓扑排序
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5017 题解 这个题目方法挺多的. 线段树优化建图 线段树优化建图的做法应该挺显然的,一个炸弹能 ...
- 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆. 现在 ...
- [SNOI2017]炸弹[线段树优化建图]
[SNOI2017]炸弹 线段树优化建图,然后跑一边tarjan把点全部缩起来,炸一次肯定是有连锁反应的所以整个连通块都一样-于是就可以发现有些是只有单向边的不能忘记更新,没了. #include & ...
- bzoj5017 炸弹 (线段树优化建图+tarjan+拓扑序dp)
直接建图边数太多,用线段树优化一下 然后缩点,记下来每个点里有多少个炸弹 然后按拓扑序反向dp一下就行了 #include<bits/stdc++.h> #define pa pair&l ...
- 『炸弹 线段树优化建图 Tarjan』
炸弹(SNOI2017) Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸 时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi ...
- BZOJ5017 [Snoi2017]炸弹[线段树优化建边+scc缩点+DAG上DP/线性递推]
方法一: 朴素思路:果断建图,每次二分出一个区间然后要向这个区间每个点连有向边,然后一个环的话是可以互相引爆的,缩点之后就是一个DAG,求每个点出发有多少可达点. 然后注意两个问题: 上述建边显然$n ...
- 炸弹:线段树优化建边+tarjan缩点+建反边+跑拓扑
这道题我做了有半个月了...终于A了... 有图为证 一句话题解:二分LR线段树优化建边+tarjan缩点+建反边+跑拓扑统计答案 首先我们根据题意,判断出来要炸弹可以连着炸,就是这个炸弹能炸到的可以 ...
- 【2019.7.26 NOIP模拟赛 T3】化学反应(reaction)(线段树优化建图+Tarjan缩点+拓扑排序)
题意转化 考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数. 于是,这个问题就被我们搬到了图上,成了一个图论题. 优化建图 考虑我们每次需要将一个区间向一个区间连边. ...
- BZOJ5017 炸弹(线段树优化建图+Tarjan+拓扑)
Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被 ...
随机推荐
- getattribute
属性访问拦截器 class Itcast(object): def __init__(self,subject1): self.subject1 = subject1 self.subject2 = ...
- Grafana介绍
Grafana是一个开源的度量分析与可视化套件.纯 Javascript 开发的前端工具,通过访问库(如InfluxDB),展示自定义报表.显示图表等.大多使用在时序数据的监控方面,如同Kibana类 ...
- Python的xml模块
先来一段xml代码 <?xml version="1.0"?> <data> <country name="Liechtenstein&qu ...
- 详解卷积神经网络(CNN)
详解卷积神经网络(CNN) 详解卷积神经网络CNN 概揽 Layers used to build ConvNets 卷积层Convolutional layer 池化层Pooling Layer 全 ...
- 第26课 可变参数模板(7)_any和variant类的实现
1. any类的实现 (1)any类: ①是一个特殊的,只能容纳一个元素的容器,它可以擦除类型,可以将何任类型的值赋值给它. ②使用时,需要根据实际类型将any对象转换为实际的对象. (2)实现any ...
- tkinter 写一个简易的ide
简易IDE 基于tkinter的简易ide,参考文档和Baidu的资料制作的,过程中遇到了很多问题,也学到了很多知识. 功能: 1.菜单栏 2.编辑功能 open save... 3.快捷键 ctr ...
- pycharm 调试django项目时,debug断点没反应???
入门python.django框架时,使用pycharm断点调试时,发现打的断点没反应,不起作用!上网上稍微一查,90%的都差不多,需要新建一个python程序,重新配置一遍,的确可以成功! 操作链接 ...
- JavaScript倒计时实现
/** * 倒计时函数 * @param {String}} endTime 终止时间戳 */ const countDown = (endTime, callback) => { const ...
- ARC下野指针 EXC_BAD_ACCESS错误
一般都是多线程造成的,某一个线程在操作一个对象时,另一个线程将此对象释放,此时就有可能造成野指针的问题.一种解决办法是如果都是UI操作则将这些操作都放在主线程去执行. 通常出现此问题的地方都在RAC, ...
- 用java语言构建一个网络服务器,实现客户端和服务器之间通信,实现客户端拥有独立线程,互不干扰
服务器: 1.与客户端的交流手段多是I/O流的方式 2.对接的方式是Socket套接字,套接字通过IP地址和端口号来建立连接 3.(曾经十分影响理解的点)服务器发出的输出流的所有信息都会成为客户端的输 ...