1. 程式人生 > >bzoj5017: [Snoi2017]炸彈

bzoj5017: [Snoi2017]炸彈

nbsp per upper rip 拓撲序 con long ont else

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。

如果一個炸彈能引爆另一個,就對應連一條有向邊,詢問即為查詢每個點能到達的點中最左和最右分別是哪一個。由於邊數較多,需要線段樹優化建圖,然後用tarjan將強連通分量縮點,縮點後在得到的DAG上逆拓撲序遞推一下。時間復雜度O(nlogn),空間復雜度O(n)(線段樹上的區間連邊不需要記錄,只要動態計算即可)。

#include<bits/stdc++.h>
typedef long long i64;
const int P=1e9+7,N=5e5+7;
char buf[N*50],*ptr=buf-1; i64 _(){ i64 x=0; int c=*++ptr,f=1; while(c<48)c==-?f=-1:0,c=*++ptr; while(c>47)x=x*10+c-48,c=*++ptr; return x*f; } int n,mx,tk=0,ss[1<<20|111],sp=0,ans=0; i64 xs[N],rs[N]; void mins(int&a,int b){if(a>b)a=b;} void maxs(int&a,int
b){if(a<b)a=b;} struct node{ int l,r,dfn,low; bool in; void chk0(node&w){ mins(l,w.l),maxs(r,w.r); } void chk1(node&w){ mins(low,w.low); chk0(w); } void chk2(node&w){ if(w.in)mins(low,w.dfn); chk0(w); } }ns[1<<20|111]; void tj(int); void tje(int w,int u){ if(!ns[u].dfn){ tj(u); ns[w].chk1(ns[u]); }else ns[w].chk2(ns[u]); } void tj(int w){ ns[w].dfn=ns[w].low=++tk; ns[w].in=1; ss[++sp]=w; if(!ns[w].l)ns[w].l=n+1; if(w<mx){ tje(w,w<<1); tje(w,w<<1^1); }else{ for(int l=mx+ns[w].l-1,r=mx+ns[w].r+1;r-l>1;l>>=1,r>>=1){ if(~l&1)tje(w,l+1); if(r&1)tje(w,r-1); } } if(ns[w].dfn==ns[w].low){ int u; do{ ns[u=ss[sp--]].in=0; ns[u].chk0(ns[w]); }while(u!=w); } } int main(){ fread(buf,1,sizeof(buf),stdin); n=_(); for(int i=1;i<=n;++i)xs[i]=_(),rs[i]=_(); for(mx=1;mx<=n+2;mx<<=1); for(int i=1;i<=n;++i){ ns[mx+i].l=std::lower_bound(xs+1,xs+n+1,xs[i]-rs[i])-xs; ns[mx+i].r=std::upper_bound(xs+1,xs+n+1,xs[i]+rs[i])-xs-1; } tj(1); for(int i=1;i<=n;++i){ node&w=ns[mx+i]; ans=(ans+i64(i)*(w.r-w.l+1))%P; } printf("%d\n",ans); return 0; }

bzoj5017: [Snoi2017]炸彈