在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi , 爆炸半径是 Ri
当一个炸弹爆炸时,如果另一个炸弹在其爆炸范围内,那么,该炸弹也会被引爆
现在,请你帮忙计算一下,先把第 i 个炸弹引爆,将引爆多少个炸弹呢?
答案对1000000007取模
要求输出的是 \(\sum_{i=1}^{n} i*ans[i]\)
这么经典的线段树优化建边,居然现在才做……
而且搞的还是玄学方法,每次lower_bound和upper_bound找其单独能炸到的所有炸弹
然后用一棵线段树来维护……
代码:
#include
#define LL long long
#define mod 1000000007
#define mid ((l+r)>>1)
#define N 500005
#define M 1000005
using namespace std;
LL n,x[N],r[N],lef[N],rig[N];
LL tl[N<<2],tr[N<<2];
LL xx,yy,ll,rr;
LL ans=0;
templateinline void read(T &res)
{
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
void build(LL rt,LL l,LL r)
{
if(l==r)
{
tl[rt]=lef[l];
tr[rt]=rig[r];
return;
}
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
tl[rt]=min(tl[rt<<1],tl[rt<<1|1]);
tr[rt]=max(tr[rt<<1],tr[rt<<1|1]);
}
void query(LL rt,LL l,LL r,LL x,LL y)
{
if(l>=x&&r<=y)
{
ll=min(ll,tl[rt]);
rr=max(rr,tr[rt]);
return;
}
if(x<=mid) query(rt<<1,l,mid,x,y);
if(y>mid) query(rt<<1|1,mid+1,r,x,y);
}
int main()
{
read(n);
for(register int i=1;i<=n;++i) read(x[i]),read(r[i]);
for(register int i=1;i<=n;++i)
{
lef[i]=lower_bound(x+1,x+i,x[i]-r[i])-x;
rig[i]=upper_bound(x+i+1,x+n+1,x[i]+r[i])-x-1;
}
build(1,1,n);
for(register int i=1;i<=n;++i)
{
xx=yy=ll=rr=i;
query(1,1,n,xx,yy);
while(ll!=xx||rr!=yy)
{
xx=ll;
yy=rr;
query(1,1,n,xx,yy);
}
ans=(LL)(ans+i*(yy-xx+1)%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}