因爲窩在作zhx的模擬題被2-SAT加線段樹優化建邊搞炸了,因此一氣之下來學了這兩個東西wwnode
這篇就是線段樹優化建邊c++
直接步入正題,先來看題目優化
在一條直線上有\(N\)個炸彈,每一個炸彈的座標是 \(X_i\),爆炸半徑是 \(R_i\),當一個炸彈爆炸時,若是另外一個炸彈所在位置\(X_j\) 知足: \(X_i-R_i\leq X_j \leq X_i+R_i\) ,那麼,該炸彈也會被引爆。 如今,請你幫忙計算一下,先把第\(i\)個炸彈引爆,將引爆多少個炸彈呢?ui
答案對\(1000000007\)取模spa
第一行,一個數字\(N\),表示炸彈個數。 第\(2\)~\(N+1\)行,每行\(2\)個數字,表示\(X_i ,R_i\),保證\(X_i\)嚴格遞增code
\(N \leq 500000 , -10^{18} \leq X_i \leq 10^{18},0 \leq R_i \leq 2 \times 10^{18}\)blog
這道題要求的其實就是引爆每個炸彈所附加的引爆炸彈的數量get
先想最暴力的方法,顯然就是對每個點進行搜索,算出他所能到達的點的數量it
那麼就能夠對可以互相到達的兩個點連邊,統計答案的時候直接進行DFS。ast
時間複雜度\(O(n^2)\),空間複雜度也是\(O(n^2)\),顯然過不了
咱們嘗試找一下這個題目特殊的性質:每個點所能覆蓋到的點在一個區間裏,也就是說這個點所能連到的點都要在這個區間裏。換句話說,對於這個區間裏的每個點都要連一條邊。而存貯這些邊是致使空間爆炸的緣由,遍歷這些邊則是致使時間爆炸的緣由。那麼要優化複雜度,顯然要優化存儲邊的方式。
接下來考慮怎麼優化?
先來看幾張圖
咱們如今要把點0向1~5這些點連邊,一共連了五條邊
可是若是咱們把這些點放到線段樹上
發現只須要把0點向區間1~5連一條邊就好了
這樣就大大減小了連邊的數量
能夠證實,給n個點連邊,經過線段樹優化的方式,複雜度由\(O(n^2)\)優化到了\(O(nlogn)\)(證實略)
連完邊以後,能夠發現可以相互到達的點必定在同一個強連通份量裏面,因而直接跑tarjan縮點,記錄每個強聯通份量裏面的節點數量,表明這個強連通份量裏面的點可以到達的點的個數。
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll ans=0;char last=' ',c=getchar(); while(c<'0'||c>'9') last=c,c=getchar(); while(c>='0'&&c<='9') ans=(ans<<3)+(ans<<1)+c-'0',c=getchar(); if(last=='-') ans=-ans; return ans; } const ll N=500050,mod=1e9+7; ll head[N<<4],Head[N<<4],ecnt,Ecnt; struct edge{ll to,nxt;}edg[N<<5],Edg[N<<5]; inline void add_edge(int u,int v) { edg[++ecnt].to=v; edg[ecnt].nxt=head[u]; head[u]=ecnt; } inline void Add_edge(int u,int v) { Edg[++Ecnt].to=v; Edg[Ecnt].nxt=Head[u]; Head[u]=Ecnt; } ll n,node; ll X[N],R[N]; ll id[N<<4],w[N<<4]; void build(int cnt,int l,int r) { if(l==r) { id[l]=cnt;node=max(node,ll(cnt));w[cnt]=1; return; } ll mid=l+r>>1; build(cnt<<1,l,mid);build(cnt<<1|1,mid+1,r); add_edge(cnt,cnt<<1),add_edge(cnt,cnt<<1|1); } void add(int cnt,int l,int r,int nl,int nr,int x) { if(nl<=l&&nr>=r) { add_edge(x,cnt); return; } ll mid=l+r>>1; if(nl<=mid) add(cnt<<1,l,mid,nl,nr,x); if(nr>mid) add(cnt<<1|1,mid+1,r,nl,nr,x); } ll dfn[N<<4],low[N<<4],ind,scc[N<<4],cnt,in[N<<4],s[N<<4],top,W[N<<4]; void tarjan(int x) { low[x]=dfn[x]=++ind; s[top++]=x; in[x]=1; for(int i=head[x];i;i=edg[i].nxt) { int v=edg[i].to; if(!dfn[v]) { tarjan(v); low[x]=min(low[x],low[v]); } else if(in[v]) low[x]=min(low[x],dfn[v]); } if(dfn[x]==low[x]) { cnt++; while(s[top]!=x) { top--; in[s[top]]=0; scc[s[top]]=cnt; W[cnt]+=w[s[top]]; } } } void rebuild() { for(int u=1;u<=node;u++) { for(int i=head[u];i;i=edg[i].nxt) { int v=edg[i].to; if(scc[u]==scc[v]) continue; Add_edge(scc[u],scc[v]); } } } queue<int> Q; ll f[N<<4],vis[N<<4]; void dfs(int u) { if(f[u]) return; vector<int> a; f[u]=W[u]; for(int i=Head[u];i;i=Edg[i].nxt) { int v=Edg[i].to; dfs(v);a.push_back(v); } for(int i=0;i<a.size();i++) { if(vis[a[i]]==u)continue; vis[a[i]]=u;f[u]+=f[a[i]]; } } ll ans=0; int main() { n=read(); for(int i=1;i<=n;i++) X[i]=read(),R[i]=read(); build(1,1,n); for(int i=1;i<=n;i++) { ll l,r; l=lower_bound(X+1,X+1+n,X[i]-R[i])-X; r=upper_bound(X+1,X+1+n,X[i]+R[i])-X-1; add(1,1,n,l,r,id[i]); } for(int i=1;i<=node;i++) if(!dfn[i]) tarjan(i); rebuild(); for(int i=1;i<=cnt;i++) dfs(i); for(int i=1;i<=n;i++) ans=(ans+i*f[scc[id[i]]])%mod; cout<<ans; }