炸弹
题目描述
输入输出格式
输入格式
输出格式
输入输出样例
输入样例 #1
4
1 1
5 1
6 5
15 15
输出样例 #1
32
N<=500000
-10e8<=x_i<=10e8
析:容易发现,只需要找到每个炸弹所能达到的最左边的炸弹和右边的炸弹即可。若暴力循环,时间复杂度为n方,考虑优化,因为要找的是最大,最小值,考虑用线段树,
用线段树存储区间炸弹,每个叶子节点即为真正的炸弹,这样可以得到每个区间内的最大最小值
存储前先预处理,二分找每个炸弹能炸到的左右最远的炸弹,储存编号
最后就是查询,先将左右边界设为自己,尝试更新,若可以,则再次尝试更新。
最后注意一下数据范围,线段树存储需要4倍空间!!!
#include<bits/stdc++.h>
#define re register int
#define ll long long
#define N 10000100
#define mo 1000000007
ll n;
ll x[N],d[N],l[N],r[N];
ll ans;
ll xx,yy,x_now,y_now;
struct TREE
{
ll tl,tr,p;
}use[N];
using namespace std;
inline long long read()
{
char c=getchar();
ll x=0,f=1;
while(c>'9'||c<'0')
{
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void build(ll k,ll L,ll R)
{
if(L==R)
{
use[k].tl=l[L];
use[k].tr=r[L];
return;
}
ll m=(L+R)>>1;
build(k<<1,L,m);
build(k<<1|1,m+1,R);
use[k].tl=min(use[k<<1].tl,use[k<<1|1].tl);
use[k].tr=max(use[k<<1].tr,use[k<<1|1].tr);
}
inline void findd(ll k,ll L,ll R,ll l,ll r)
{
if(l<=L&&R<=r)
{
x_now=min(x_now,use[k].tl);
y_now=max(y_now,use[k].tr);
return;
}
ll m=(L+R)>>1;
if(m>=l)
findd(k<<1,L,m,l,r);
if(m<r)
findd(k<<1|1,m+1,R,l,r);
}
int main()
{
//scanf("%lld",&n);
n=read();
for(re i=1;i<=n;i++)
{
x[i]=read();
d[i]=read();
}
// scanf("%lld%lld",&x[i],&d[i]);
for(re i=1;i<=n;i++)
{
l[i]=lower_bound(x+1,x+i+1,x[i]-d[i])-x;
r[i]=upper_bound(x+i+1,x+n+1,x[i]+d[i])-x-1;
}
build(1,1,n);
for(register long long i=1;i<=n;i++)
{
xx=yy=x_now=y_now=i;
findd(1,1,n,xx,yy);
while(xx!=x_now||yy!=y_now)
{
xx=x_now;
yy=y_now;
findd(1,1,n,xx,yy);
}
ans=(ans+(y_now-x_now+1)*i%mo)%mo;
}
printf("%lld",ans%mo);
return 0;
}