因为窝在作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; }