• [SNOI2017]炸弹(线段树优化建图)


    题目链接

    很直观的想法是直接把每个点向能炸到的点都连一条边,然后缩点dag再dp一下求出每个点能炸到的最左端和最右端。

    可是这样空间and时间复杂度都爆炸,直接gg。

    怎么优化它呢?

    我们发现每个点能引爆的点一定在某个区间内,所以我们容懿想到线段树优化建图。

    建一棵线段树,从父节点连向它的两个子节点一条有向边。

    对于每个节点,计算出它自己爆炸的范围,然后向线段树上代表这个区间的那些点连边。

    这个节点其实用线段树上的叶节点即可,不用再建新的节点。

    然后跑一遍tarjan强连通分量缩点,每个新点要记录改分量里能炸到的最远的左右端点。

    之后我们还有跑一点dag dp,用每个节点能到达的点更新其左右端点。

    注意这里不能直接拓扑,要在反图上拓扑,因为这里直接拓扑时孩子是没有更新的(虽然能过。。。)

    查询时直接返回对应scc的左右端点的差即可。

    上代码:

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const ll N = 5000010;
    const ll mod = 1e9 + 7;
    struct bomb{
        ll pos, r;
    }a[N];
    ll id[N];
    ll n;
    ll dfn[N], dep, low[N], stk[N], top;
    ll col[N], scc;
    ll mx;
    bool vis[N];
    ll L[N], R[N], Left[N], Right[N], ans;
    vector<ll> vec[N], G[N];
    void add(int u, int v) {
        G[u].push_back(v);
    }
    void build(ll p, ll l, ll r) {
        mx = max(mx, p);
        L[p] = l;
        R[p] = r;
        if (l == r) {
            id[l] = p;
            return;
        }
        ll mid = (l + r) >> 1;
        build(p << 1, l, mid);
        build(p << 1 | 1, mid + 1, r);
        add(p, p << 1);
        add(p, p << 1 | 1);
    }
    void work(ll p, ll l, ll r, ll x, ll y, ll v) {
        if (r < x || l > y) return;
        if (x <= l && r <= y) {
            if (v == p) return;
            add(v, p);
            return;
        }
        ll mid = (l + r) >> 1;
        work(p << 1, l, mid, x, y, v);
        work(p << 1 | 1, mid + 1, r, x, y, v); 
    }
    void tarjan(ll x) {
        dfn[x] = low[x] = ++dep;
        stk[++top] = x;
        vis[x] = 1;
        for (ll i = 0; i < G[x].size(); i++) {
            ll y = G[x][i];
            if (!dfn[y]) {
                tarjan(y);
                low[x] = min(low[x], low[y]);
            } else if (vis[y]) {
                low[x] = min(low[x], dfn[y]);
            }
        }
        if (dfn[x] == low[x]) {
            col[x] = ++scc;
            vis[x] = 0;
            Left[scc] = L[x];
            Right[scc] = R[x];
            while (stk[top] != x) {
                Left[scc] = min(Left[scc], L[stk[top]]);
                Right[scc] = max(Right[scc], R[stk[top]]);
                col[stk[top]] = scc;
                vis[stk[top]] = 0;
                top--;
            }
            top--;
        }
    }
    ll Find1(ll l, ll r, ll v) {
        ll ret = 0;
        while (l <= r) {
            ll mid = (l + r) >> 1;
            if (a[mid].pos <= v) {
                ret = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return ret;
    }
    ll Find2(ll l, ll r, ll v) {
        ll ret = 0;
        while (l <= r) {
            ll mid = (l + r) >> 1;
            if (a[mid].pos >= v) {
                ret = mid;
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return ret;
    }
    void dfs(int x) {
        vis[x] = 1;
        for (int i = 0; i < vec[x].size(); i++) {
            int y = vec[x][i];
            if (vis[y]) {
                Left[x] = min(Left[x], Left[y]);
                Right[x] = max(Right[x], Right[y]);
                continue;
            }
            dfs(y);
            Left[x] = min(Left[x], Left[y]);
            Right[x] = max(Right[x], Right[y]);
        }
    }
    ll query(ll x) {
        return Right[x] - Left[x] + 1;
    }
    bool cmp(bomb p, bomb q) {
        return p.pos < q.pos;
    }
    int main() {
        scanf("%lld", &n);
        for (ll i = 1; i <= n; i++) {
            scanf("%lld%lld", &a[i].pos, &a[i].r);
        }
        sort(a + 1, a + 1 + n, cmp);
        build(1, 1, n);
        for (ll i = 1; i <= n; i++) {
            if (a[i].r == 0) continue;
            ll r = Find1(i, n, a[i].pos + a[i].r);
            ll l = Find2(1, i, a[i].pos - a[i].r);
            work(1, 1, n, l, r, id[i]);
            Left[id[i]] = l;
            Right[id[i]] = r;
        }
        tarjan(1);
        for (ll x = 1; x <= mx; x++) {
            for (ll j = 0; j < G[x].size(); j++) {
                ll y = G[x][j];
                if (col[x] != col[y]) {
                    vec[col[x]].push_back(col[y]);
                }
            }
        }
        for (int i = 1; i <= scc; i++) {
            sort(vec[i].begin(), vec[i].end());
            unique(vec[i].begin(), vec[i].end());
        }
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= scc; i++) {
            if (!vis[i]) dfs(i);
        }
        for (ll i = 1; i <= n; i++){
            ans += (i * query(col[id[i]])) % mod;
            ans %= mod;
        }
        cout << ans % mod;
        return 0;
    }
  • 相关阅读:
    P6057 [加油武汉]七步洗手法
    LC 1349. Maximum Students Taking Exam (Hungarian / Max Flow)
    P1879 [USACO06NOV]玉米田Corn Fields
    P1433 吃奶酪 (TSP)
    LC 1349. Maximum Students Taking Exam
    获取XML中的值
    TimeZoneInfo类的使用
    XML 克隆节点
    网络协议概述:物理层、连接层、网络层、传输层、应用层详解
    Vuex
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/12860496.html
Copyright © 2020-2023  润新知