【SNOI2017】炸弹

南笙酒味 提交于 2019-12-01 05:28:48
在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi , 爆炸半径是 Ri
当一个炸弹爆炸时,如果另一个炸弹在其爆炸范围内,那么,该炸弹也会被引爆
现在,请你帮忙计算一下,先把第 i 个炸弹引爆,将引爆多少个炸弹呢?
答案对1000000007取模

要求输出的是 \(\sum_{i=1}^{n} i*ans[i]\)

这么经典的线段树优化建边,居然现在才做…… 而且搞的还是玄学方法,每次lower_bound和upper_bound找其单独能炸到的所有炸弹 然后用一棵线段树来维护……

代码:

#include<bits/stdc++.h>
#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;

template<class T>inline 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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!