Petrozavodsk Winter 2018 - A. Mines - 线段树优化建图、强连通分量缩点、DP

题意: 一维数轴上有 n n n 个雷。第 i i i 个雷在位置 p i p_i pi。 花费 c i c_i ci 的代价可以引爆第 i i i 个雷,并将区间 [ p i − r i , p i + r i ] [p_i-r_i,p_i+r_i] [piri,pi+ri] 的范围的雷全部引爆,引起连锁反应而不需要额外的代价。现在又 q q q 次修改,每次修改一个雷的花费,然后询问使得所有雷爆炸的最小花费是多少。

  • 1 ≤ n , q ≤ 200 , 000 1\le n,q\le 200,000 1n,q200,000
  • 1 ≤ p i , r i , c i ≤ 1 , 000 , 000 , 000 1\le p_i,r_i,c_i\le 1,000,000,000 1pi,ri,ci1,000,000,000

题解: 建一棵线段树,每个父亲节点向左右儿子分别连一条有向边。然后枚举每一个雷,从 p i p_i pi 所在的叶子节点,向 [ p i − r i , p i + r i ] [p_i-r_i,p_i+r_i] [piri,pi+ri] 对应的节点连边,tarjan 求一次 scc。删掉所有可以从含有叶子节点的 scc 到达的 scc,剩下的每个包含叶子的 scc 的最小值之和就是答案。修改可以每个 scc 维护一个 multiset。时间复杂度 O ( n log ⁡ n ) \mathcal{O}(n\log{n}) O(nlogn)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const int maxm = maxn << 2;
vector<int> g[maxm], h[maxm];
int dfn[maxm], low[maxm], belong[maxm], scc, cnt;
bool vis[maxm];
stack<int> s;
void tarjan(int u) {
    low[u] = dfn[u] = ++cnt;
    s.push(u);
    vis[u] = 1;
    for (int v: g[u]) {
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (vis[v]) low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u]) {
        ++scc;
        int v;
        do {
            v = s.top();
            s.pop();
            vis[v] = 0;
            belong[v] = scc;
        } while (u != v);
    }
}
int id[maxn];
struct SegmentTree {
#define ls (rt<<1)
#define rs (rt<<1|1)
    void build(int l, int r, int rt) {
        if (l == r) {
            id[l] = rt;
            return;
        }
        int m = (l + r) / 2;
        g[rt].push_back(ls);
        g[rt].push_back(rs);
        build(l, m, ls);
        build(m+1, r, rs);
    }

    void query(int l, int r, int p, int L, int R, int rt) {
        if (l <= L && R <= r) {
            g[p].push_back(rt);
            return;
        }
        int m = (L + R) / 2;
        if (l <= m) query(l, r, p, L, m, ls);
        if (r > m) query(l, r, p, m+1, R, rs);
    }
} T;
struct node {
    int p, r, c;
    friend bool operator< (const node& x, const node& y) {
        return x.p < y.p;
    }
} a[maxn];
int rk[maxn], in[maxm];
vector<pair<int, int>> dx;
multiset<int> ms[maxm];
bool ins[maxm];
int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d", &a[i].p, &a[i].r, &a[i].c);
        dx.push_back({a[i].p, i});
    }
    sort(dx.begin(), dx.end());
    T.build(1, n, 1);
    for (int i = 1; i <= n; i++) {
        rk[i] = lower_bound(dx.begin(), dx.end(), make_pair(a[i].p, i)) - dx.begin() + 1;
        int l = lower_bound(dx.begin(), dx.end(), make_pair(a[i].p - a[i].r, 0)) - dx.begin() + 1;
        int r = lower_bound(dx.begin(), dx.end(), make_pair(a[i].p + a[i].r + 1, 0)) - dx.begin();
        T.query(l, r, id[rk[i]], 1, n, 1);
    }
    int m = 4 * n;
    for (int i = 1; i <= m; i++) {
        if (!dfn[i]) tarjan(i);
    }
    for (int i = 1; i <= scc; i++) vis[i] = 0;
    for (int i = 1; i <= n; i++) {
        vis[belong[id[rk[i]]]] = 1;
        ms[belong[id[rk[i]]]].insert(a[i].c);
    }
    queue<int> Q;
    for (int u = 1; u <= m; u++) {
        for (int v: g[u]) {
            if (belong[u] != belong[v]) {
                h[belong[u]].push_back(belong[v]);
                if (vis[belong[u]]) {
                    ++in[belong[v]];
                    if (!vis[belong[v]] && !ins[belong[v]]) {
                        Q.push(belong[v]);
                        ins[v] = 1;
                    }
                }
            }
        }
    }
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        for (int v: h[u]) {
            in[v] += in[u];
            if (!ins[v]) {
                Q.push(v);
                ins[v] = 1;
            }
        }
        ins[u] = 0;
    }
    ll sum = 0;
    for (int i = 1; i <= scc; i++) {
        if (vis[i] && in[i] == 0) sum += *ms[i].begin(); 
    }
    while (q--) {
        int x, y;
        scanf("%d%d", &x, &y);
        if (!in[belong[id[rk[x]]]]) {
            sum -= *ms[belong[id[rk[x]]]].begin();
            auto it = ms[belong[id[rk[x]]]].find(a[x].c);
            ms[belong[id[rk[x]]]].erase(it);
            ms[belong[id[rk[x]]]].insert(y);
            sum += *ms[belong[id[rk[x]]]].begin();
            a[x].c = y;
        }
        printf("%lld\n", sum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值