Codeforces2020D Connect the Dots(观察 + 并查集 + 差分)

题意

多组数据。数轴上有 n 个点,编号为 1n,对这些点做 m 次操作。
每次操作给出三个整数 ai(1ain)   di(1di10)   ki(0kin)。将点 ai,ai+di,ai+2×di,ai+3×di,,ai+ki×di两两相连。问做完所有的操作之后,有多少个连通块。

数据范围:Σ n2×105,Σ m2×105

题解

对于连通块相关的问题,通常考虑用并查集合并。但是如果暴力合并的话,时间复杂度为 O(nm),显然通过不了这道题。

仔细观察发现,d上界很小,只有 10。不妨对这些操作按照 d 的大小分组,然后每一组单独考虑。

对于合并操作,我们只需要标记一下,哪些点需要和前面(或后面)的邻点合并,最后再统一处理。

对于标记,我们可以差分处理。

时间复杂度为 O(d×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;
}
posted @   yanhy-orz  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示