5017: [Snoi2017]炸弹
Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 608 Solved: 190
[Submit][Status][Discuss]
Description
在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足:
Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆。
现在,请你帮忙计算一下,先把第 i 个炸弹引爆,将引爆多少个炸弹呢?
Input
第一行,一个数字 N,表示炸弹个数。
第 2∼N+1行,每行 2 个数字,表示 Xi,Ri,保证 Xi 严格递增。
N≤500000
−10^18≤Xi≤10^18
0≤Ri≤2×10^18
Output
一个数字,表示Sigma(i*炸弹i能引爆的炸弹个数),1<=i<=N mod10^9+7。
Sample Input
4
1 1
5 1
6 5
15 15
1 1
5 1
6 5
15 15
Sample Output
32
HINT
Source
显然一个点可以引爆的炸弹是一个连续的区间,对于每一个点我们向他可以引爆的最左边的炸弹到最右边的炸弹连边。可以使用线段树优化建图。
我们对建出来的图tarjan缩点,每个点维护minl和maxr。
对于这个图按拓扑序倒序dp,求出每个点的minl和maxr,统计答案即可。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define maxn 500005
#define ll long long
#define mod 1000000007
using namespace std;
inline ll read() {
char ch=getchar();ll x=,f=;
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-;
for(;isdigit(ch);ch=getchar()) x=x*+ch-'';
return x*f;
}
int n,rt;
ll s[maxn*],a[maxn*],id[maxn*],tsz;
struct seg {int s[];ll mn,mx;}t[maxn*];
struct Edge {int fr,to,nxt;}e[maxn*];
int head[maxn*],sz;
void addedge(int u,int v) {e[sz].fr=u;e[sz].nxt=head[u];e[sz].to=v;head[u]=sz++;}
void build(int l,int r,int &x) {
x=++tsz;
if(l==r) {id[l]=x;t[x].mn=t[x].mx=l;return;}
int mid=l+r>>;
build(l,mid,t[x].s[]);build(mid+,r,t[x].s[]);
if(t[x].s[]) addedge(x,t[x].s[]);
if(t[x].s[]) addedge(x,t[x].s[]);
t[x].mn=l;t[x].mx=r;
return ;
}
void add(int l,int r,int x,int L,int R,int p) {
if(L<=l&&R>=r) {if(id[p]==x) return;addedge(id[p],x);return;}
int mid=l+r>>;
if(L<=mid) add(l,mid,t[x].s[],L,R,p);
if(R>mid) add(mid+,r,t[x].s[],L,R,p);
return;
}
bool inq[maxn*];
ll dfn[maxn*],low[maxn*],tim,q[maxn*],top,lm[maxn*],rm[maxn*];
int bel[maxn*],scc;
void tarjan(int x) {
dfn[x]=low[x]=++tim;q[++top]=x;inq[x]=;
for(int i=head[x];i>=;i=e[i].nxt) {
int to=e[i].to;
if(!dfn[to]){
tarjan(to);low[x]=min(low[x],low[to]);
}else if(inq[to]) low[x]=min(low[x],dfn[to]);
}
if(dfn[x]==low[x]) {
scc++;
lm[scc]=214748364700000000ll;
ll now;
do {
now=q[top--];inq[now]=;bel[now]=scc;
lm[scc]=min(lm[scc],t[now].mn);rm[scc]=max(rm[scc],t[now].mx);
}while(now!=x);
}
}
ll rd[maxn*];
int main() {
//freopen("bomb9.in","r",stdin);
memset(head,-,sizeof(head));
n=read();
for(int i=;i<=n;i++) {s[i]=read();a[i]=read();}
build(,n,rt);
for(int i=;i<=n;i++) {
int now=lower_bound(s+,s+n+,s[i])-s,L=lower_bound(s+,s+n+,s[i]-a[i])-s,R=upper_bound(s+,s+n+,s[i]+a[i])-s-;
if(L==R) continue;add(,n,,L,R,i);
}
for(int i=;i<=tsz;i++) if(!dfn[i]) {tarjan(i);}
int tmp=sz;sz=;memset(head,-,sizeof(head));
for(int i=;i<tmp;i++) {
int u=bel[e[i].fr],v=bel[e[i].to];
if(u==v) continue;
rd[v]++;addedge(u,v);
}
int hd=,tl=;
for(int i=;i<=scc;i++) if(!rd[i]) q[tl++]=i;
while(hd!=tl) {
int now=q[hd++];
for(int i=head[now];i>=;i=e[i].nxt) {
int to=e[i].to;rd[to]--;
if(!rd[to]) q[tl++]=to;
}
}
for(int i=scc;i>=;i--) {
int now=q[i];
for(int j=head[now];j>=;j=e[j].nxt) {
int to=e[j].to;
lm[now]=min(lm[now],lm[to]);rm[now]=max(rm[now],rm[to]);
}
}
ll ans=;
for(int i=;i<=n;i++) {
ans+=(ll)i*(rm[bel[id[i]]]-lm[bel[id[i]]]+);ans%=mod;
}
printf("%lld\n",ans);
}