• sss


    <更新提示>

    <第一次更新>


    <正文>

    炸弹(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

    4 
    1 1 
    5 1 
    6 5 
    15 15
    

    Sample Output

    32
    

    解析

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

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

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

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

    (Code:)

    #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
    ",ans);
        return 0;
    }
    
    

    <后记>

  • 相关阅读:
    【设计】概要设计-详细设计-到底需要输出什么???
    【Java】Eclipse代码格式化-代码模板
    【Scala】Scala学习资料
    【Java】阿里巴巴Java开发手册(纪念版)
    【Storm】学习笔记
    【HBase】学习笔记
    【Hadoop】Combiner的本质是迷你的reducer,不能随意使用
    【Hadoop】mapreduce采用多进程与spark采用多线程比较
    【ES】elasticsearch学习笔记
    【MySQL】MySQL统计NULL字段处理
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11197019.html
Copyright © 2020-2023  润新知