[SNOI2017]炸弹

题目:BZOJ5017。

题目大意:
有一些炸弹排成一行,每个炸弹都有一个位置\(X_i\)和爆炸半径\(R_i\)。一个炸弹爆炸时,处在其爆炸范围内的其他炸弹会跟着爆炸。
即炸弹\(i\)爆炸时,对任意一个炸弹\(j\)(\(j\neq i\)),若\(X_i-R_i\leqslant X_j\leqslant X_i+R_i\),则炸弹\(j\)爆炸。

设引爆炸弹\(i\)将会使\(b_i\)个炸弹爆炸(包括其本身),求\(\sum\limits _{i=1}^n i\times b_i\)。

解题思路:
设\(l_i,r_i\)分别表示引爆炸弹\(i\)时最左边爆炸的炸弹和最右边爆炸的炸弹,初始为\(i\)。
首先更新\(l\)。
若\(X_i-X_{l_i -1}\leqslant R_i\),则炸弹\(l_i-1\)能被连锁反应到,则用\(l_{l_i -1}\)更新\(l_i\)。然后若\(R_{l_i}-(X_i -X_{l_i})>R_i\),则更新\(R_i\)(已经计算对左边的贡献,所以\(R_i\)保存向右炸的长度即可)。
同理更新\(r_i\)即可,由于\(R_i\)已经最大,所以\(r_i\)更新出来肯定最优。同时令\(l_i=max \{ l_i,l_{r_i} \} \)(可能右边那个炸弹炸得更左边)。
然后计算答案即可。时间复杂度\(O(n)\)。

C++ Code:

#include<bits/stdc++.h>
#define N 500005
#define LoveLive long long
int l[N],r[N],n;
LoveLive x[N],R[N];
template<typename T>
inline T max(const T x,const T y){return x>y?x:y;}
template<typename T>
inline T min(const T x,const T y){return x<y?x:y;}
inline LoveLive readint(){
	int c=getchar(),f=0;
	LoveLive d=0;
	for(;!isdigit(c);c=getchar())f=c=='-';
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^'0');
	return f?-d:d;
}
int main(){
	n=readint();
	for(int i=1;i<=n;++i)x[i]=readint(),R[i]=readint(),l[i]=r[i]=i;
	for(int i=2;i<=n;++i)
	while(l[i]>1&&x[i]-x[l[i]-1]<=R[i])
	l[i]=l[l[i]-1],R[i]=max(R[i],R[l[i]]-(x[i]-x[l[i]]));
	for(int i=n-1;i;--i)
	while(r[i]<n&&x[r[i]+1]-x[r[i]]<=R[i])
	r[i]=r[r[i]+1],l[i]=min(l[i],l[r[i]]);
	LoveLive ans=0;
	for(int i=1;i<=n;++i)
	ans=(ans+(r[i]-l[i]+1LL)*i)%1000000007;
	std::cout<<ans;
	return 0;
}

 

posted @ 2018-06-13 16:32  Mrsrz  阅读(369)  评论(0编辑  收藏  举报