码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2017-09-09 23:15:06      阅读:370      评论:0      收藏:0      [点我收藏+]

标签:void   多少   algorithm   tail   --   for   text   单调栈   ios   

[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。

输入示例

4
1 1
5 1
6 5
15 15

输出示例

32

数据规模及约定

见“输入

题解

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

考虑一种容易漏掉的情况:一个炸弹 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]炸弹

标签:void   多少   algorithm   tail   --   for   text   单调栈   ios   

原文地址:http://www.cnblogs.com/xiao-ju-ruo-xjr/p/7499602.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!