【并查集+贪心】CF Edu152 D

Problem - D - Codeforces

题意:

【并查集+贪心】CF Edu152 D_第1张图片

【并查集+贪心】CF Edu152 D_第2张图片

思路:

首先观察样例可知,我们可以把连续的非0序列看作一个连通块,次数能够最少是因为同一个连通块的可以一次性染色

又观察到,一个连通块,有2和没2也是不一样的

如果有2,那么连通块两端可以和0结合,如果没有2,可以选其中一端和0结合

贪心地从有2的连通块开始染色,再从普通连通块染色,然后剩下的的单独染即可

因为维护了左边第一个0和右边第一个0,注意要判一下边界

Code:

#include 

#define int long long

using i64 = long long;

constexpr int N = 2e5 + 10;
constexpr int M = 2e5 + 10;
constexpr int P = 2600;
constexpr i64 Inf = 1e18;
constexpr int mod = 1e9 + 7;
constexpr double eps = 1e-6;

int n;
int a[N];
int f[N], vis[N];
int pre[N], suf[N];

int find(int x) {
    return f[x] = (x == f[x]) ? x : find(f[x]);
}
void join(int u, int v) {
    int f1 = find(u), f2 = find(v);
    if (f1 != f2) {
        f[f1] = f2;
    }
}
void solve() {
    std::cin >> n;

    std::vector V, V1;
    for (int i = 1; i <= n; i ++) {
        std::cin >> a[i];
        if (a[i] == 2) V.push_back(i);
        if (a[i] == 1) V1.push_back(i);
    }

    pre[0] = 0;
    suf[n + 1] = n + 1;
    for (int i = 1; i <= n; i ++) {
        if (a[i] == 0) pre[i] = i;
        else pre[i] = pre[i - 1];
    }

    for (int i = n; i >= 1; i --) {
        if (a[i] == 0) suf[i] = i;
        else suf[i] = suf[i + 1];
    }

    for (int i = 1; i <= n; i ++) {
        f[i] = i;
    }

    int l = 1, r = 1;
    while(1) {
        if (l > n || r > n) break;
        while(l <= n && a[l] == 0) l ++;
        r = l;
        while(r <= n && a[r] != 0) {
            r ++;
            if (a[r] != 0 && r - 1 >= 1) {
                join(r, r - 1);
            }
        }
        l = r;
    }

    for (auto x : V) {
        if (!vis[find(x)]) {
            vis[find(x)] = 1;
            if (pre[x] >= 1 && !vis[find(pre[x])]) {
                vis[find(pre[x])] = 1;
                join(x, pre[x]);
            }
            if (suf[x] <= n && !vis[find(suf[x])]) {
                vis[find(suf[x])] = 1;
                join(x, suf[x]);
            } 
        }
    }

    for (auto i : V1) {
        if (!vis[find(i)]) {
            vis[find(i)] = 1;
            bool ok = false;
            if (!vis[find(pre[i])] && pre[i] >= 1) {
                ok = true;
                vis[find(pre[i])] = 1;
                join(i, pre[i]);
            }
            if (!ok) {
                if (!vis[find(suf[i])] && suf[i] <= n) {
                    ok = true;
                    vis[find(suf[i])] = 1;
                    join(i, suf[i]);
                }
            }
        }
    }

    int ans = 0;
    for (int i = 1; i <= n; i ++) {
        if (find(i) == i) ans ++;
    }

    std::cout << ans << "\n";
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int t = 1;

    while (t--) {
        solve();
    }
    
    return 0;
}

你可能感兴趣的:(贪心,并查集,算法)