[LOJ#2255][BZOJ5017][Snoi2017]炸弹

试题描述

在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: 
Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆。 
现在,请你帮忙计算一下,先把第 i 个炸弹引爆,将引爆多少个炸弹呢? 

输入

第一行,一个数字 N,表示炸弹个数。 
第 2∼N+1行,每行 2 个数字,表示 Xi,Ri,保证 Xi 严格递增。 
N≤500000
−10^18≤Xi≤10^18
0≤Ri≤2×10^18

输出

一个数字,表示Sigma(i*炸弹i能引爆的炸弹个数),1<=i<=N mod10^9+7。

输入示例


输出示例


数据规模及约定

见“输入

题解

显然一个炸弹能引爆的范围一定是一段连续的区间,于是我们就考虑求它的左右端点。

考虑一种容易漏掉的情况:一个炸弹 a 引爆左边一个炸弹 b,b 引爆 a 右侧的 c,c 引爆 b 左侧的 d……这种情况我们不难发现从 a 到 d,炸弹的爆炸半径一定倍增(比如若 b 的半径小于 a 半径的两倍,由于 b 可以引爆 a 右边的 c,所以 a 可以直接引爆 c,不需要借助 b)。

剩下的情况就是连锁爆炸(即爆炸只往一个方向传递),处理这个东西我们只需要用单调栈正反扫一遍处理出每个炸弹向左向右连锁爆炸能炸到的最远的炸弹就可以了(不妨设向左向右最远的炸弹编号分别为 lft[i] 和 rgt[i])。

最后我们用 RMQ 维护一下 lft[i] 的最小值,rgt[i] 的最大值;若要求炸弹 i 的范围,就是不停扩张的过程,最多扩张 log(n) 次。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define LL long long const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
LL read() {
LL x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
} #define maxn 500010
#define maxlog 19
#define MOD 1000000007 int n, q[maxn], top, lft[maxn], rgt[maxn];
LL X[maxn], R[maxn]; int Log[maxn], mn[maxlog][maxn], mx[maxlog][maxn];
void init() {
for(int i = 1; i <= n; i++) mn[0][i] = lft[i], mx[0][i] = rgt[i];
for(int j = 1; (1 << j) <= n; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
mn[j][i] = min(mn[j-1][i], mn[j-1][i+(1<<j-1)]),
mx[j][i] = max(mx[j-1][i], mx[j-1][i+(1<<j-1)]);
return ;
}
int _l, _r;
void query(int ql, int qr) {
int t = Log[qr-ql+1];
_l = min(mn[t][ql], mn[t][qr-(1<<t)+1]);
_r = max(mx[t][ql], mx[t][qr-(1<<t)+1]);
return ;
} int main() {
n = read();
for(int i = 1; i <= n; i++) X[i] = read(), R[i] = read(); Log[1] = 0;
for(int i = 2; i <= n; i++) Log[i] = Log[i>>1] + 1; lft[1] = 1;
q[top = 1] = 1;
for(int i = 2; i <= n; i++) {
int l = 1, r = top;
while(l < r) {
int mid = l + r >> 1;
if(X[q[mid]] < X[i] - R[i]) l = mid + 1; else r = mid;
}
if(X[q[l]] < X[i] - R[i]) lft[i] = i;
else lft[i] = lft[q[l]];
while(top && lft[i] <= lft[q[top]]) top--;
q[++top] = i;
}
rgt[n] = n;
q[top = 1] = n;
for(int i = n - 1; i; i--) {
int l = 1, r = top;
while(l < r) {
int mid = l + r >> 1;
if(X[q[mid]] > X[i] + R[i]) l = mid + 1; else r = mid;
}
// printf("%d: %d | %d %lld\n", i, l, q[l], X[q[l]]);
if(X[q[l]] > X[i] + R[i]) rgt[i] = i;
else rgt[i] = rgt[q[l]];
while(top && rgt[i] >= rgt[q[top]]) top--;
q[++top] = i;
}
// for(int i = 1; i <= n; i++) printf("LR [%d %d]\n", lft[i], rgt[i]);
init();
int ans = 0;
for(int i = 1; i <= n; i++) {
int l = lft[i], r = rgt[i];
_l = n + 1; _r = 0;
for(;;) {
query(l, r);
if(l == _l && r == _r) break;
l = _l; r = _r;
}
ans += ((LL)i * (r - l + 1)) % MOD;
if(ans >= MOD) ans -= MOD;
// printf("[%d, %d]\n", l, r);
} printf("%d\n", ans); return 0;
}

[LOJ#2255][BZOJ5017][Snoi2017]炸弹的更多相关文章

  1. loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点

    loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点 链接 loj 思路 用交错关系建出图来,发现可以直接缩点,拓扑统计. 完了吗,不,瓶颈在于边数太多了,线段树优化建图. 细节 ...

  2. loj #2255. 「SNOI2017」炸弹

    #2255. 「SNOI2017」炸弹 题目描述 在一条直线上有 NNN 个炸弹,每个炸弹的坐标是 XiX_iX​i​​,爆炸半径是 RiR_iR​i​​,当一个炸弹爆炸时,如果另一个炸弹所在位置 X ...

  3. [bzoj5017][Snoi2017]炸弹 tarjan缩点+线段树优化建图+拓扑

    5017: [Snoi2017]炸弹 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 608  Solved: 190[Submit][Status][ ...

  4. BZOJ5017 Snoi2017炸弹(线段树+强连通分量+缩点+传递闭包)

    容易想到每个炸弹向其能引爆的炸弹连边,tarjan缩点后bitset传递闭包.进一步发现每个炸弹能直接引爆的炸弹是一段连续区间,于是线段树优化建图即可让边的数量降至O(nlogn).再冷静一下由于能间 ...

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

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

  6. bzoj千题计划311:bzoj5017: [Snoi2017]炸弹(线段树优化tarjan构图)

    https://www.lydsy.com/JudgeOnline/problem.php?id=5017 暴力: 对于每一个炸弹,枚举所有的炸弹,看它爆炸能不能引爆那个炸弹 如果能,由这个炸弹向引爆 ...

  7. BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan

    Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...

  8. bzoj5017: [Snoi2017]炸弹

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

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

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

随机推荐

  1. iOS消息体系架构详解-融云即时通讯云

    iOS SDK 体系架构 本文档将详细介绍融云的 SDK 产品架构和消息体系,以便于您更深入的了解融云并更快速的开发自己的产品. 融云 SDK 系统架构 IMKit IMKit 的功能主要是封装各种界 ...

  2. Android(java)学习笔记117:SharedPreferences(轻量级存储类)

    1.SharedPreferences是Android平台上一个轻量级的存储类,简单的说就是可以存储一些我们需要的变量信息.2个activity 之间的数据传递除了可以他通过intent来传递数据,还 ...

  3. python3.6.2利用pyinstaller发布EXE

    我的环境是Ubuntu 16.04,系统自带Python2和Python3 安装 pip3 install pyinstaller 发布exe pyinstaller -F helloworld.py ...

  4. java基础—object类

    一.Object类介绍

  5. stataic 变量

    static 是静态变量的的类型说明符 静态变量属于静态存储方式,(外部变量也是静态存储方式) 静态的局部变量 静态局部变量属于静态存储方式,它具有以下特点: (1)静态局部变量在函数内定义 它的生存 ...

  6. 【转】pDc->SelectObject(pOldBrush)恢复画刷

    请看下面的代码:  CDC *pDc=new CClientDC(this); CBrush brush; brush.CreateSolidBrush(RGB(0,255,0)); CBrush * ...

  7. NOIP模拟赛 密室逃脱

    密室逃脱(maze.*) 即使czhou没有派出最强篮球阵容,机房篮球队还是暴虐了校篮球队.为了不打击校篮球队信心,czhou决定改变训练后的活动.近来,江大掌门的徒弟徒孙们纷纷事业有成,回到母校为机 ...

  8. Leetcode 20 有效的括号valid-parentheses(栈)

    给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. 注意空字符串可被认 ...

  9. 数据结构实用C语言基础

    大纲: 主要介绍了C语言中的指针,内存分配,两种传参方式,typedef的简单用法 关于C语言中的指针: 指针变量也称为指针(Pointer) 例如:int* p; 则p为一个指向int类型的指针. ...

  10. 安装ElasticSearch 6.1.1 head插件

    https://blog.csdn.net/zoubf/article/details/79007908 主要参考了这个blog 才完成所有的配置,很好的参考资料