[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; }