zoukankan      html  css  js  c++  java
  • 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序

    题目描述

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

    输入

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

    输出

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

    样例输入

    4
    1 1
    5 1
    6 5
    15 15

    样例输出

    32


    题解

    线段树优化建图+Tarjan+拓扑排序

    看到题第一眼想到图论,然而边数爆炸难以承受,由于一个炸弹覆盖的是一个区间,因此想到使用线段树优化建图来解决。

    建完图问的就是一个点最多能够遍历多少个点,直接Tarjan缩点+拓扑排序递推是无法统计的,因为状态会转移重复。

    但是考虑到本题有一个特殊的性质:一个炸弹能够引爆的所有炸弹(包括连锁反应)一定也是一段区间,对于区间只需要求出区间左右端点的位置即可。

    因此对于每个点维护它的位置,要求的就是一个点能够经过的所有点的位置最大&最小值。由于最值是可以重复统计的,因此Tarjan缩点,然后建反图按拓扑序递推即可。最后直接使用二分查找找出一个炸弹覆盖的个数。

    时间复杂度$O(nlog n)$,貌似这不是正解,但是复杂度是对的,给代码1K-的dalao跪了。。。

    #include <queue>
    #include <cstdio>
    #include <algorithm>
    #define N 500010
    #define lson l , mid , x << 1
    #define rson mid + 1 , r , x << 1 | 1
    using namespace std;
    queue<int> q;
    long long a[N] , v[N] , mn[N * 4] , mx[N * 4] , vmin[N * 4] , vmax[N * 4];
    int n , pos[N] , head[N * 4] , to[N * 40] , next[N * 40] , cnt;
    int deep[N * 4] , low[N * 4] , tot , ins[N * 4] , sta[N * 4] , top , bl[N * 4] , num;
    int hh[N * 4] , tt[N * 40] , nn[N * 40] , cc , rd[N * 4];
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    void build(int l , int r , int x)
    {
    	if(l == r)
    	{
    		pos[l] = x;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	mn[x] = 1ll << 62 , mx[x] = -1ll << 62;
    	build(lson) , build(rson);
    	add(x , x << 1) , add(x , x << 1 | 1);
    }
    void update(int b , int e , int p , int l , int r , int x)
    {
    	if(b <= l && r <= e)
    	{
    		add(p , x);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(b <= mid) update(b , e , p , lson);
    	if(e > mid) update(b , e , p , rson);
    }
    void tarjan(int x)
    {
    	int i;
    	deep[x] = low[x] = ++tot , ins[x] = 1 , sta[++top] = x;
    	for(i = head[x] ; i ; i = next[i])
    	{
    		if(!deep[to[i]]) tarjan(to[i]) , low[x] = min(low[x] , low[to[i]]);
    		else if(ins[to[i]]) low[x] = min(low[x] , deep[to[i]]);
    	}
    	if(deep[x] == low[x])
    	{
    		int t;
    		num ++ , vmin[num] = 1ll << 62 , vmax[num] = -1ll << 62;
    		do
    		{
    			t = sta[top -- ] , ins[t] = 0 , bl[t] = num;
    			vmin[num] = min(vmin[num] , mn[t]) , vmax[num] = max(vmax[num] , mx[t]);
    		}while(t != x);
    	}
    }
    int main()
    {
    	int n , i , x;
    	long long ans = 0;
    	scanf("%d" , &n);
    	build(1 , n , 1);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%lld%lld" , &a[i] , &v[i]) , mn[pos[i]] = mx[pos[i]] = a[i];
    	a[n + 1] = 1ll << 62;
    	for(i = 1 ; i <= n ; i ++ )
    		update(lower_bound(a + 1 , a + n + 2 , a[i] - v[i]) - a , upper_bound(a + 1 , a + n + 2 , a[i] + v[i]) - a - 1 , pos[i] , 1 , n , 1);
    	for(i = 1 ; i <= n * 4 ; i ++ )
    		if(!deep[i])
    			tarjan(i);
    	for(x = 1 ; x <= n * 4 ; x ++ )
    		for(i = head[x] ; i ; i = next[i])
    			if(bl[x] != bl[to[i]])
    				tt[++cc] = bl[x] , nn[cc] = hh[bl[to[i]]] , hh[bl[to[i]]] = cc , rd[bl[x]] ++ ;
    	for(i = 1 ; i <= num ; i ++ )
    		if(!rd[to[i]])
    			q.push(to[i]);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		for(i = hh[x] ; i ; i = nn[i])
    		{
    			vmin[tt[i]] = min(vmin[tt[i]] , vmin[x]) , vmax[tt[i]] = max(vmax[tt[i]] , vmax[x]) , rd[tt[i]] -- ;
    			if(!rd[tt[i]]) q.push(tt[i]);
    		}
    	}
    	for(i = 1 ; i <= n ; i ++ )
    		ans = (ans + (long long)(upper_bound(a + 1 , a + n + 1 , vmax[bl[pos[i]]]) - lower_bound(a + 1 , a + n + 1 , vmin[bl[pos[i]]])) * i) % 1000000007;
    	printf("%lld
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    Redis学习(一)认识并安装redis
    并发编程(七)线程如何优雅地终止
    并发编程(六)Object类中线程相关的方法详解
    并发编程(五)线程同步
    并发编程(四)Thread类详解
    并发编程(三)线程池
    并发编程(二)Java中的多线程
    Python学习————作业
    Python学习————作业(面向对象)
    Python学习————面向对象和面向过程
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7515348.html
Copyright © 2011-2022 走看看