Codeforces2020D Connect the Dots(觀察 + 並查集 + 差分)

yanhy-orz發表於2024-10-04

題意

多組資料。數軸上有 \(n\) 個點,編號為 \(1 \sim n\),對這些點做 $ m $ 次操作。
每次操作給出三個整數 \(a_i(1 \le a_i \le n) \ \ \ d_i(1 \le d_i \le 10) \ \ \ k_i(0 \le k_i \le n)\)。將點 \(a_i, a_i + d_i, a_i + 2 \times d_i, a_i + 3 \times d_i, \cdot\cdot\cdot, a_i + k_i \times d_i\)兩兩相連。問做完所有的操作之後,有多少個連通塊。

資料範圍:\(\Sigma \ n \le 2 \times 10^5, \Sigma \ m \le 2 \times 10^5\)

題解

對於連通塊相關的問題,通常考慮用並查集合併。但是如果暴力合併的話,時間複雜度為 \(\mathcal{O}(n m)\),顯然透過不了這道題。

仔細觀察發現,\(d\)上界很小,只有 \(10\)。不妨對這些操作按照 \(d\) 的大小分組,然後每一組單獨考慮。

對於合併操作,我們只需要標記一下,哪些點需要和前面(或後面)的鄰點合併,最後再統一處理。

對於標記,我們可以差分處理。

時間複雜度為 \(\mathcal{O}(d \times n)\)

點選檢視程式碼
#include <cstdio>
#include <iostream>

using i64 = long long;

inline int read() {
    int res = 0; bool sym = false; char ch = getchar();
    while (ch < '0' || ch > '9') sym |= ch == '-', ch = getchar();
    while (ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch & 15), ch = getchar();
    return sym ? -res : res;
}

constexpr int N = 2e5 + 50, M = 10;

int T, n, m, a, d, k, tag[M + 1][N], par[N], ans;

int find(int x) {
    return par[x] == x ? x : par[x] = find(par[x]);
}

void merge(int x, int y) {
    x = find(x); y = find(y); if (x != y) par[y] = x;
}

void solve() {
    n = read(); m = read(); ans = 0;
    for (int i = 1; i <= n; i++) {
        par[i] = i;
    }
    for (int d = 1; d <= M; d++) {
        for (int i = 1; i <= n; i++) {
            tag[d][i] = 0;
        }
    }

    for (int i = 1; i <= m; i++) {
        a = read(); d = read(); k = read();
        if (k) {
		    // 差分
            tag[d][a + d]++;
            tag[d][a + (k + 1) * d]--;
        }
    }

    for (int d = 1; d <= M; d++) {
        for (int i = 1; i <= n; i++) {
            if (i - d >= 1) {
                tag[d][i] += tag[d][i - d];
            }
        }
    }

    for (int d = 1; d <= M; d++) {
        for (int i = 1; i <= n; i++) {
            if (tag[d][i]) {
                merge(i, i - d);
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        if (find(i) == i) {
            ans++;
        }
    }
    printf("%d\n", ans);
}

int main() {
    for (T = read(); T; T--) {
        solve();
    }
    return 0;
}

相關文章