洛谷 P5025 [SNOI2017]炸弹 线段树优化建图
题目描述
在一条直线上有 \(n\) 个炸弹,每个炸弹的坐标是 \(x_i\) ,爆炸半径是 \(r_i\),当一个炸弹爆炸时,如果另一个炸弹所在位置 \(x_j\) 满足: \(|x_j-x_i| \le r_i\) ,那么,该炸弹也会被引爆。
现在,请你帮忙计算一下,先把第 \(i\) 个炸弹引爆,将引爆多少个炸弹呢?
答案对 \(10^9 + 7\) 取模
输入格式
第一行,一个数字 \(n\),表示炸弹个数。 第 \(2 \sim n+1\) 行,每行两个整数,表示 \(x_i\),\(r_i\),保证 \(x_i\) 严格递增。
输出格式
一个数字,表示 \(\sum \limits_{i=1}^n i\times 炸弹 i 能引爆的炸弹个数\)。
输入输出样例
输入 #1
4
1 1
5 1
6 5
15 15
输出 #1
32
说明/提示
【数据范围】
\(n≤500000\),
\(-10^{18}≤x_i≤10^{18}\),
\(0≤r_i≤2 \times 10^{18}\)
分析
如果炸弹\(i\)可以炸到炸弹\(j\),那么我们就建一条从\(i\)到\(j\)的有向边
那么从\(i\)出发,经过的所有点都可以被\(i\)炸到
我们可以缩点,然后跑一边拓扑排序记录答案
每一个点需要记录的就是它能炸到的最左边的点的坐标和最右边的点的坐标
如果直接建边,边数有可能达到\(n^2\)条,所以要用线段树优化建图
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
const int maxn=1e7+5;
const int mod=1e9+7;
const long long INF=1e18;
long long x[maxn],r[maxn],L[maxn],R[maxn];
int n,head[maxn],tot=1,h2[maxn],t2=1;
struct asd{
int from,to,next;
}b[maxn],b2[maxn];
void ad(int aa,int bb){
b[tot].from=aa;
b[tot].to=bb;
b[tot].next=head[aa];
head[aa]=tot++;
}
void ad2(int aa,int bb){
b2[t2].from=aa;
b2[t2].to=bb;
b2[t2].next=h2[aa];
h2[aa]=t2++;
}
struct trr{
int l,r;
}tr[maxn];
int id[maxn],dfn[maxn],low[maxn],dfnc,sta[maxn],top,shuyu[maxn],js,maxcnt,rk[maxn];
void build(int da,int l,int r){
if(da>maxcnt) maxcnt=da;
tr[da].l=l,tr[da].r=r;
if(tr[da].l==tr[da].r){
id[l]=da;
rk[da]=l;
return;
}
int mids=(l+r)>>1;
build(da<<1,l,mids);
build(da<<1|1,mids+1,r);
ad(da,da<<1);
ad(da,da<<1|1);
}
void xg(int da,int l,int r,int w){
if(tr[da].l>=l && tr[da].r<=r){
ad(w,da);
return;
}
int mids=(tr[da].l+tr[da].r)>>1;
if(l<=mids) xg(da<<1,l,r,w);
if(r>mids) xg(da<<1|1,l,r,w);
}
void tar(int now){
dfn[now]=low[now]=++dfnc;
sta[++top]=now;
for(int i=head[now];i!=-1;i=b[i].next){
int u=b[i].to;
if(!dfn[u]){
tar(u);
low[now]=std::min(low[now],low[u]);
} else if(!shuyu[u]){
low[now]=std::min(low[now],dfn[u]);
}
}
if(low[now]==dfn[now]){
js++;
while(1){
int y=sta[top--];
shuyu[y]=js;
if(rk[y]!=0){
L[js]=std::min(L[js],x[rk[y]]-r[rk[y]]);
R[js]=std::max(R[js],x[rk[y]]+r[rk[y]]);
}
if(y==now) break;
}
}
}
int f[maxn],rd[maxn];
std::queue<int> q;
void tp(){
for(int i=1;i<=js;i++){
if(rd[i]==0) q.push(i);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=h2[now];i!=-1;i=b2[i].next){
int u=b2[i].to;
L[u]=std::min(L[u],L[now]);
R[u]=std::max(R[u],R[now]);
rd[u]--;
if(rd[u]==0) q.push(u);
}
}
}
int main(){
memset(head,-1,sizeof(head));
memset(h2,-1,sizeof(h2));
for(int i=0;i<maxn;i++){
L[i]=INF,R[i]=-INF;
}
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x[i],&r[i]);
}
build(1,1,n);
for(int i=1;i<=n;i++){
int le=std::lower_bound(x+1,x+1+n,x[i]-r[i])-x;
int ri=std::upper_bound(x+1,x+1+n,x[i]+r[i])-x-1;
xg(1,le,ri,id[i]);
}
for(int i=1;i<=maxcnt;i++){
if(!dfn[i]) tar(i);
}
for(int i=1;i<=maxcnt;i++){
for(int j=head[i];j!=-1;j=b[j].next){
int u=b[j].to;
if(shuyu[i]!=shuyu[u]){
ad2(shuyu[u],shuyu[i]);
rd[shuyu[i]]++;
}
}
}
tp();
for(int i=1;i<=js;i++){
int le=std::lower_bound(x+1,x+1+n,L[i])-x;
int ri=std::upper_bound(x+1,x+1+n,R[i])-x-1;
f[i]=ri-le+1;
}
long long ans=0;
for(int i=1;i<=n;i++){
ans=(ans+1LL*i*f[shuyu[id[i]]]%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}