Petrozavodsk Winter 2018 - A. Mines - 線段樹優化建圖、強連通分量縮點、DP

題意: 一維數軸上有 nn 個雷。第 ii 個雷在位置 pip_i。 花費 cic_i 的代價可以引爆第 ii 個雷,並將區間 [piri,pi+ri][p_i-r_i,p_i+r_i] 的範圍的雷全部引爆,引起連鎖反應而不需要額外的代價。現在又 qq 次修改,每次修改一個雷的花費,然後詢問使得所有雷爆炸的最小花費是多少。

  • 1n,q200,0001\le n,q\le 200,000
  • 1pi,ri,ci1,000,000,0001\le p_i,r_i,c_i\le 1,000,000,000

題解: 建一棵線段樹,每個父親節點向左右兒子分別連一條有向邊。然後枚舉每一個雷,從 pip_i 所在的葉子節點,向 [piri,pi+ri][p_i-r_i,p_i+r_i] 對應的節點連邊,tarjan 求一次 scc。刪掉所有可以從含有葉子節點的 scc 到達的 scc,剩下的每個包含葉子的 scc 的最小值之和就是答案。修改可以每個 scc 維護一個 multiset。時間複雜度 O(nlogn)\mathcal{O}(n\log{n})

代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章