Loading [MathJax]/jax/output/CommonHTML/jax.js

『炸弹 线段树优化建图 Tarjan』

Parsnip·2019-07-16 19:35·293 次阅读

『炸弹 线段树优化建图 Tarjan』

<更新提示>

<第一次更新>


<正文>

炸弹(SNOI2017)#

Description#

在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸 时,如果另一个炸弹所在位置 Xj 满足:
Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆。 现在,请你帮忙计算一下,先把第 i 个炸弹引爆,将引爆多少个炸弹呢?

Input Format#

第一行,一个数字 N,表示炸弹个数。 第 2∼N+1行,每行 2 个数字,表示 Xi,Ri,保证 Xi 严格递增。
N≤500000
−10 ^18≤Xi≤10 ^18
0≤Ri≤2×10 ^18

Output Format#

一个数字,表示Sigma(i*炸弹i能引爆的炸弹个数),1<=i<=N mod10^9+7。

Sample Input#

Copy
4 1 1 5 1 6 5 15 15

Sample Output#

Copy
32

解析#

很自然我们可以将问题转化为图论的模型:每个炸弹当做一个点,向可以连环引爆的其他点连边,然后一个点的答案就是这个点出发dfs可以遍历到的所有点。

直接连边的话建图就会超时,边数的n2的,不难发现每一个点要连边的点处于连续的一段区间中,于是想到线段树优化建图。

什么是线段树优化建图?就是把线段树用邻接表显式的建出来,然后对于一个区间内的连续若干个点的连边,就可以利用线段树区间划分的方式,向不超过log2(rl+1)个线段树节点连边,以达到向这当中所有点连边的目的。

建完图后,我们又发现对每一个点都dfs会超时,于是想到将互相可达的点先处理掉,也就是SCC缩点,然后在剩下的DAG上,反图dp即可统计每一个点的答案。在这当中,我们需要维护一下每个点可达区间的最小左端点和最大右端点即可。

Code:

Copy
#include <bits/stdc++.h> using namespace std; const int N = 500020 , Mod = 1000000007; struct edge { int ver,next; } e1[N*25],e2[N*25]; struct SegmentTree { int l,r,id,re; #define l(p) ver[p].l #define r(p) ver[p].r #define id(p) ver[p].id #define re(p) ver[p].re }ver[N<<2]; int n,t1,t2,Head1[N*2],Head2[N*2],indeg[N*2],tot; int dfn[N*2],low[N*2],ins[N*2],st[N*2],c[N*2],top,num,cnt; int Min[N*2],Max[N*2]; long long x[N],r[N],ans; inline void insert1(int x,int y) { e1[++t1] = (edge){y,Head1[x]} , Head1[x] = t1; } inline void insert2(int x,int y) { e2[++t2] = (edge){y,Head2[x]} , Head2[x] = t2; } inline void chmin(int &a,int b) { a = min( a , b ); } inline void chmax(int &a,int b) { a = max( a , b ); } inline void input(void) { scanf("%d",&n); for ( int i = 1; i <= n; i++ ) scanf("%lld%lld",&x[i],&r[i]); } inline void BuildTree(int p,int l,int r) { l(p) = l , r(p) = r; if ( l == r ) { id(p) = l , re(l) = p; return; } id(p) = ++tot , re(tot) = p; int mid = l + r >> 1; BuildTree( p<<1 , l , mid ); BuildTree( p<<1|1 , mid+1 , r ); insert1( id(p) , id(p<<1) ); insert1( id(p) , id(p<<1|1) ); } inline void connect(int p,int l,int r,int x) { if ( l <= l(p) && r >= r(p) ) return insert1( x , id(p) ); int mid = l(p) + r(p) >> 1; if ( l <= mid ) connect( p<<1 , l , r , x ); if ( r > mid ) connect( p<<1|1 , l , r , x ); } inline void Tarjan(int x) { dfn[x] = low[x] = ++num; st[++top] = x , ins[x] = true; for ( int i = Head1[x]; i; i = e1[i].next ) { int y = e1[i].ver; if ( !dfn[y] ) { Tarjan( y ); low[x] = min( low[x] , low[y] ); } else if ( ins[y] ) low[x] = min( low[x] , dfn[y] ); } if ( dfn[x] == low[x] ) { ++cnt; int y; do { y = st[top--] , ins[y] = false; c[y] = cnt; chmin( Min[cnt] , l(re(y)) ); chmax( Max[cnt] , r(re(y)) ); } while ( x != y ); } } inline void Topsort(void) { queue < int > q; for ( int i = 1; i <= tot; i++ ) if ( !indeg[i] ) q.push(i); while ( !q.empty() ) { int x = q.front(); q.pop(); for ( int i = Head2[x]; i; i = e2[i].next ) { int y = e2[i].ver; chmin( Min[y] , Min[x] ); chmax( Max[y] , Max[x] ); if ( ! -- indeg[y] ) q.push( y ); } } } inline void BuildGraph(void) { for ( int i = 1; i <= n; i++ ) { int L = lower_bound( x+1 , x+i+1 , x[i] - r[i] ) - x; int R = upper_bound( x+i+1 , x+n+1 , x[i] + r[i] ) - x - 1; connect( 1 , L , R , i ); } } inline void rebuild(void) { for ( int x = 1; x <= tot; x++ ) { for ( int i = Head1[x]; i; i = e1[i].next ) { int y = e1[i].ver; if ( c[x] != c[y] ) insert2( c[y] , c[x] ) , indeg[c[x]]++; } } } inline void solve(void) { for ( int i = 1; i <= n; i++ ) ans = ( ans + 1LL * i * ( Max[c[i]] - Min[c[i]] + 1 ) % Mod ) % Mod; } int main(void) { input(); tot = n , BuildTree( 1 , 1 , n ); BuildGraph(); memset( Min , 0x3f , sizeof Min ); memset( Max , 0x00 , sizeof Max ); for ( int i = 1; i <= tot; i++ ) if ( !dfn[i] ) Tarjan( i ); rebuild(); Topsort(); solve(); printf("%lld\n",ans); return 0; }

<后记>

posted @   Parsnip  阅读(293)  评论(0)    收藏  举报
编辑推荐:
· 微服务架构学习与思考:微服务拆分的原则
· 记一次 .NET某云HIS系统 CPU爆高分析
· 如果单表数据量大,只能考虑分库分表吗?
· 一文彻底搞懂 MCP:AI 大模型的标准化工具箱
· 电商平台中订单未支付过期如何实现自动关单?
阅读排行:
· 精选 4 款免费且实用的数据库管理工具,程序员必备!
· Cursor:一个让程序员“失业”的AI代码搭子
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(6)
· 重生之我是操作系统(七)----内存管理(上)
· .NET 阻止Windows关机以及阻止失败的一些原因
目录
点击右上角即可分享
微信分享提示