P5025 [SNOI2017]炸弹 线段树优化建图+缩点+DAG图上DP

Posted kaka0010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P5025 [SNOI2017]炸弹 线段树优化建图+缩点+DAG图上DP相关的知识,希望对你有一定的参考价值。

原题链接:https://www.luogu.com.cn/problem/P5025

题意

在一个数轴上有n个点代表n颗炸弹,每颗炸弹引爆会引发r范围内的炸弹爆炸,问每颗炸弹引爆会最终引发多少颗炸弹爆炸,对答案 ∑ i = 1 n i ∗ 炸 弹 引 爆 数 量 \\sum_{i=1}^ni*炸弹引爆数量 i=1ni%1e9+7

分析

锻炼代码能力的一道题,首先需要用到一个前置知识——线段树优化建图点击查看

然后我们就可以连出所有这个炸弹能到的点,然后对在同一个强连通分量里的点进行缩点,这样可以让原图变成DAG图,我们处理出每个连通块可以到达的左右边界,然后在dfs遍历时更新当前点的左右边界就可以了,记得在回溯时更新。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const ll INF = 1e9;
const int N = 5e5 + 10;
const int M = 3e6 + 10;
const int MOD = 1e9 + 7;
vector<int> g[N<<2], G[N<<2];
int tot, id[N<<2];
int stk[N<<2], vis[N<<2], low[N<<2], dfn[N<<2], idx, tp, scc;
int Left[N<<2], Right[N<<2], col[N<<2];
struct node {
    int l, r;
}t[N<<2], a[N<<2];
void build(int u, int l, int r) {
    t[u].l = l, t[u].r = r;
    if (l == r) {
        id[u] = l;
        return;
    }
    int mid = (l + r) >> 1;
    build(u<<1, l, mid);
    build(u<<1|1, mid+1, r);
    id[u] = ++tot;
    a[id[u]] = {l, r};
    g[id[u]].push_back(id[u<<1]);
    g[id[u]].push_back(id[u<<1|1]);
}
void add_edge(int u, int ql, int qr, int v) {
    if (ql <= t[u].l && qr >= t[u].r) {
        g[v].push_back(id[u]);
        return;
    }
    int mid = (t[u].l + t[u].r) >> 1;
    if (ql <= mid) add_edge(u<<1, ql, qr, v);
    if (qr > mid) add_edge(u<<1|1, ql, qr, v);
}
void tarjan(int x) {
    low[x] = dfn[x] = ++idx;
    vis[x] = 1;
    stk[++tp] = x;
    for (auto v : g[x]) {
        if (!dfn[v]) {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        } else if (vis[v]) {
            low[x] = min(low[x], dfn[v]);
        }
    }
    if (low[x] == dfn[x]) {
        int y;
        ++scc;
        while (y = stk[tp--]) {
            col[y] = scc;
            vis[y] = 0;
            Left[scc] = min(Left[scc], a[y].l);
            Right[scc] = max(Right[scc], a[y].r);
            if (x == y) break;
        }
    }
}
void dfs(int x) {
    vis[x] = 1;
    for (auto v : G[x]) {
        if (!vis[v]) dfs(v);
        Left[x] = min(Left[x], Left[v]);
        Right[x] = max(Right[x], Right[v]);
    }
}
ll x[N], r[N];
void solve() {
    int n; cin >> n;
    tot = n;
    build(1, 1, n);
    for (int i = 1; i <= n; i++) cin >> x[i] >> r[i];
    for (int i = 1; i <= n; i++) {
        if (!r[i]) {
            a[i] = {i, i};
            continue;
        }
        int L = lower_bound(x + 1, x + n + 1, x[i] - r[i]) - x;
        int R = upper_bound(x + 1, x + n + 1, x[i] + r[i]) - x - 1;
        add_edge(1, L, R, i);
        a[i] = {L, R};
    }
    memset(Left, 0x3f, sizeof Left);
    for (int i = 1; i <= tot; i++) if (!dfn[i]) tarjan(i);
    for (int i = 1; i <= tot; i++) {
        for (auto v : g[i]) {
            if (col[v] == col[i]) continue;
            G[col[i]].push_back(col[v]);
        }
    }
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= scc; i++) if (!vis[i]) dfs(i);
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
//        cout << Right[col[i]] << " " <<  Left[col[i]] << endl;
        ans += 1ll*i * (Right[col[i]] - Left[col[i]] + 1) % MOD;
        ans %= MOD;
    }
    cout << ans << endl;
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}

以上是关于P5025 [SNOI2017]炸弹 线段树优化建图+缩点+DAG图上DP的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj5017][Snoi2017]炸弹 tarjan缩点+线段树优化建图+拓扑

BZOJ 5017 [Snoi2017]炸弹

[LOJ#2255][BZOJ5017][Snoi2017]炸弹

bzoj5017: [Snoi2017]炸弹

[CERC2017]Intrinsic Interval[scc+线段树优化建图]

codeforces 787D - Legacy 线段树优化建图,最短路