P5025 [SNOI2017]炸弹 题解

P5025 [SNOI2017]炸弹

在数轴上给定n个炸弹,每个炸弹有位置和爆炸半径,如果爆炸半径内有别的炸弹,那么爆炸时就会引爆它们,依次求出从某个炸弹起爆会引起多少个炸弹爆炸.\(n \leq 5*10^5\)

容易发现,本题与之前的CF1158C异曲同工.只是本题更为直观.

考虑二分查找出每个炸弹起爆能引发的爆炸区间,从该炸弹向引爆的炸弹连边,最后dfs过程中统计能到达的结点中最左和最右在哪即可.

由于n较大,连边复杂度过高,考虑线段树优化连边.显然连边之后,对于每个强联通分量,引爆其中任何一个都是引爆整个强联通分量,使用tarjan在线段树上缩点建新图,在新图上跑dfs即可.

思维难度不大,注意代码细节即可:

  • 初始化不能只到n,原因在于线段树缩点后点数可能依然多于原图(卡了半天).

  • 数字较大,注意long long 和取模.

  • dfs时注意一次性从前后更新所有经过的结点,而不是依次dfs,那样复杂度就boom了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <map>
    #include <cstring>
    #include <assert.h>
    using namespace std;
    const long long N = 4 * 500005, INF = 0x3f3f3f3f3f3f3f3f, Mod = 1000000007;
    long long n, sum, tot, scc, top, ll, rr, totseg;
    long long head1[N], cnt1, head2[N], cnt2, loc[N], range[N], pos[N], dfn[N], low[N], stack[N], in_sta[N], bel[N], L[N], R[N], vis[N];
    map<long long, long long> m[N];
    struct point
    {
        long long l, r;
    } p[N * 4];
    struct edge
    {
        long long to, next;
    } e1[N * 4], e2[N * 4];
    void add1(long long u, long long v)
    {
        e1[++cnt1].to = v;
        e1[cnt1].next = head1[u];
        head1[u] = cnt1;
    }
    void add2(long long u, long long v)
    {
        e2[++cnt2].to = v;
        e2[cnt2].next = head2[u];
        head2[u] = cnt2;
    }
    void build(long long k, long long l, long long r)
    {
        p[k] = (point){l, r};
        totseg = max(totseg, k);
        if (l == r)
        {
            pos[l] = k;
            return;
        }
        long long mid = (l + r) / 2;
        build(k * 2, l, mid);
        build(k * 2 + 1, mid + 1, r);
        add1(k, k * 2);
        add1(k, k * 2 + 1);
    }
    void range_add(long long k, long long l, long long r, long long u)
    {
        if (ll <= l && rr >= r)
        {
            if (k == u)
                return;
            add1(u, k);
            return;
        }
        long long mid = (l + r) / 2;
        if (ll <= mid)
            range_add(k * 2, l, mid, u);
        if (rr > mid)
            range_add(k * 2 + 1, mid + 1, r, u);
    }
    void tarjan(long long k)
    {
        dfn[k] = low[k] = ++tot;
        stack[++top] = k;
        in_sta[k] = 1;
        for (long long i = head1[k]; i; i = e1[i].next)
        {
            long long v = e1[i].to;
            if (!dfn[v])
            {
                tarjan(v);
                low[k] = min(low[k], low[v]);
            }
            else if (in_sta[v])
                low[k] = min(low[k], dfn[v]);
        }
        if (low[k] == dfn[k])
        {
            scc++;
            while (stack[top + 1] != k)
            {
                bel[stack[top]] = scc;
                in_sta[stack[top]] = 0;
                L[scc] = min(L[scc], p[stack[top]].l);
                R[scc] = max(R[scc], p[stack[top]].r);
                top--;
            }
        }
    }
    void dfs(long long k)
    {
        vis[k] = 1;
        for (long long i = head2[k]; i; i = e2[i].next)
        {
            long long v = e2[i].to;
            if (vis[v])
            {
                L[k] = min(L[k], L[v]);
                R[k] = max(R[k], R[v]); //前驱也可以更新后继
                continue;
            }
            dfs(v);
            L[k] = min(L[k], L[v]);
            R[k] = min(R[k], R[v]); //后继更新前驱
        }
    }
    void build_new_graph()
    {
        for (long long i = 1; i <= totseg; i++)
            for (long long j = head1[i]; j; j = e1[j].next)
            {
                long long v = e1[j].to;
                if (bel[i] != bel[v] && !m[bel[i]][bel[v]])
                {
                    m[bel[i]][bel[v]] = 1;
                    add2(bel[i], bel[v]);
                }
            }
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin >> n;
        assert(n <= 500000);
        for (long long i = 1; i <= n; i++)
        {
            cin >> loc[i] >> range[i];
            L[i] = INF;
        }
        memset(L, 0x3f, sizeof(L));
        loc[n + 1] = INF;
        build(1, 1, n);
        for (long long i = 1; i <= n; i++)
        {
            if (!range[i])
                continue;
            ll = lower_bound(loc + 1, loc + 1 + n, loc[i] - range[i]) - loc;
            rr = upper_bound(loc + 1, loc + 1 + n, loc[i] + range[i]) - loc - 1;
            range_add(1, 1, n, pos[i]);
            p[pos[i]] = (point){ll, rr};
        }
        tarjan(1);
        build_new_graph();
        for (long long i = 1; i <= scc; i++)
            if (!vis[i])
                dfs(i);
        for (long long i = 1; i <= n; i++)
            sum = (sum + ((long long)(R[bel[pos[i]]] - L[bel[pos[i]]] + 1) % Mod * i) % Mod) % Mod;
        cout << sum % Mod << endl;
    }
    
    
posted @ 2021-11-06 15:01  Kinuhata  阅读(65)  评论(0)    收藏  举报