题目:BZOJ5017。
题目大意:
有一些炸弹排成一行,每个炸弹都有一个位置(X_i)和爆炸半径(R_i)。一个炸弹爆炸时,处在其爆炸范围内的其他炸弹会跟着爆炸。
即炸弹(i)爆炸时,对任意一个炸弹(j)((j
eq i)),若(X_i-R_ileqslant X_jleqslant X_i+R_i),则炸弹(j)爆炸。
设引爆炸弹(i)将会使(b_i)个炸弹爆炸(包括其本身),求(sumlimits _{i=1}^n i imes 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;
}