题目: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; }