• SNOI2017 炸弹


    题目链接

    Solution

    如果把每个炸弹看成点,将它向所有它能引爆的炸弹连有向边,那么图中同一个强连通分量就代表其中所有的炸弹可以相互引爆。这样我们对连边之后的图进行缩点,就能获得一个 DAG。

    可以发现,缩点之后的每个点都代表了一段炸弹区间;而每个点最终能引爆的炸弹也形成一段区间。

    因此,我们可以在 tarjan 的过程中记下每个点表示的左端点(Minl)和右端点(Maxr),然后反向建图进行拓扑排序,若 v->u,用 (Minl_v) 来更新 (Minl_u),用 (Maxr_v) 来更新 (Maxr_u),最终求得每个点实际能引爆的区间。然后我们就能知道每个炸弹能引爆的炸弹数,统计答案即可。

    但是数据范围 (5 * 10^5),暴力建图的时间和空间复杂度都过高。考虑到是要向区间连边,可以使用线段树优化建图。如下图:

    (线段树上每个节点向它的两个儿子连边)

    如果我们要 2 节点向 [4,7] 连边,就可以采取如上方式,让 2->4,2->6,2->7。这个例子的优化不是很明显,但是一旦数据增大,可以将建图复杂度优化到 (log) 级别。

    可以看出,线段树优化建图跟线段树的关系不大,只是采取了线段树的形式。

    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #define LL long long
    using namespace std;
    
    const int N = 666666, mod = 1e9 + 7;
    struct edge { LL nxt, to, f; } e[N << 3], f[N << 3];
    LL n, id = 0, top = 0, cnt = 0, cnt2 = 0, Ans = 0, color = 0;
    LL st[N << 2], du[N << 2], vis[N << 2], dfn[N << 2], low[N << 2], col[N << 2], head[N << 2];
    LL ind[N], h2[N << 2], Maxr[N << 2], x[N], r[N], Minl[N << 2];
    
    void add(LL x, LL y) { e[++cnt] = (edge) { head[x], y, x }, head[x] = cnt; }
    void add2(LL x, LL y) { f[++cnt2] = (edge) { h2[x], y, x }, h2[x] = cnt2; }
    
    void build(LL x, LL l, LL r)
    {
        if(l == r)
        {
            ind[l] = x;
            return ;
        }
        LL mid = (l + r) >> 1;
        build(x << 1, l, mid);
        build(x << 1 | 1, mid + 1, r);
        add(x, x << 1);
        add(x, x << 1 | 1);
    }
    void update(LL x, LL l, LL r, LL stdl, LL stdr, LL std)
    {
        if(l > stdr || r < stdl) return ;
        if(stdl <= l && stdr >= r)
        {
            if(x != std) add(std, x);
            return ;
        }
        LL mid = (l + r) >> 1;
        update(x << 1, l, mid, stdl, stdr, std);
        update(x << 1 | 1, mid + 1, r, stdl, stdr, std);
    }
    
    void tarjan(LL x)
    {
        dfn[x] = low[x] = ++id;
        st[++top] = x, vis[x] = 1;
        for(LL i = head[x]; i; i = e[i].nxt)
        {
            LL v = e[i].to;
            if(!dfn[v]) tarjan(v), low[x] = min(low[x], low[v]);
            else if(vis[v]) low[x] = min(low[x], dfn[v]);
        }
        if(dfn[x] == low[x])
        {
            color++, st[top + 1] = -1;
            while(st[top + 1] != x)
                vis[st[top]] = 0, col[st[top--]] = color;
        }
    }
    void top_sort()
    {
        queue <int> q; 
        for(LL i = 1; i <= color; i++) if(du[i] == 0) q.push(i);
        while(!q.empty())
        {
            LL a = q.front();
            q.pop();
            for(LL i = h2[a]; i; i = f[i].nxt)
            {
                LL v = f[i].to;
                Minl[v] = min(Minl[v], Minl[a]);
                Maxr[v] = max(Maxr[v], Maxr[a]);
                du[v]--;
                if(du[v] == 0) q.push(v);
            }
        } 
    }
    
    void Clear()
    {
        memset(du, 0, sizeof(du));
        memset(h2, 0, sizeof(h2));
        memset(vis, 0, sizeof(vis));
        memset(low, 0, sizeof(low));
        memset(dfn, 0, sizeof(dfn));
        memset(head, 0, sizeof(head));
        memset(Maxr, 0, sizeof(Maxr));
        memset(Minl, 0x3f, sizeof(Minl));
        return ;
    }
    
    int main()
    {
        scanf("%lld", &n);
        Clear(), build(1, 1, n);
        for(LL i = 1; i <= n; i++) scanf("%lld%lld", &x[i], &r[i]);
        for(LL i = 1; i <= n; i++)
        {
            LL il = lower_bound(x + 1, x + n + 1, x[i] - r[i]) - x;
            LL ir = upper_bound(x + 1, x + n + 1, x[i] + r[i]) - x - 1;
            update(1, 1, n, il, ir, ind[i]);
        }
        tarjan(1);
        for(LL i = 1; i <= n; i++)
        {
            Minl[col[ind[i]]] = min(Minl[col[ind[i]]], i);
            Maxr[col[ind[i]]] = max(Maxr[col[ind[i]]], i);
        }
        for(LL i = 1; i <= cnt; i++)
            if(col[e[i].f] != col[e[i].to])
                add2(col[e[i].to], col[e[i].f]), du[col[e[i].f]]++;
        top_sort();
        for(LL i = 1; i <= n; i++)
            Ans = (Ans + i * (Maxr[col[ind[i]]] - Minl[col[ind[i]]] + 1) + mod) % mod;
        printf("%lld", Ans % mod);
        return 0;
    }
    
  • 相关阅读:
    处理sevenzipsharp 检查密码函数的Bug
    C# 开源压缩组件比较
    css 一些技巧
    input 限制输入
    原生JS实现淡入淡出效果(fadeIn/fadeOut/fadeTo)
    js string.format 方法
    Atom插件及使用
    chrome浏览器的跨域设置-包括版本49前后两种设置 ,windows&mac
    原生js监听input值改变事件
    html5 tab横向滚动,无滚动条(transform:translate)
  • 原文地址:https://www.cnblogs.com/Andy-park/p/13772535.html
Copyright © 2020-2023  润新知