Cod sursa(job #3182176)

Utilizator vlad.ulmeanu30Ulmeanu Vlad vlad.ulmeanu30 Data 8 decembrie 2023 18:31:00
Problema Parcurgere DFS - componente conexe Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 3.28 kb
#include <bits/stdc++.h>

using namespace std;

const int max_log2 = 17;

struct Edge {
    int a, b;

    Edge(): a(0), b(0) {}
    Edge(int a, int b) : a(a), b(b) {}
};

int main() {
    ///algoritmul lui Reif (Random Mate).

    std::ifstream fin("dfs.in");
    std::ofstream fout("dfs.out");

    int n, m; fin >> n >> m;

    Edge *edges = new Edge[m]();

    for (int i = 0; i < m; i++) {
        int a, b; fin >> a >> b;
        edges[i] = Edge(a-1, b-1);
    }

    int *parent = new int[n](), *ancestors = new int[n](), *ancestorsNext = new int[n]();
    bool *orientation = new bool[n]();

    std::iota(parent, parent + n, 0);

    bool workLeft = true;
    while (workLeft) {
        workLeft = false;

        for (int i = 0; i < n; i++) {
            orientation[i] = rand() & 1;
            ///daca orientarea e 0, presupun ca nodul e copil; pentru 1, presupun ca e parinte.
            ///mai tarziu ma uit la un nod copil si vad daca are un vecin parinte. il unesc cu unul dintre ei.
        }

        ///fac stramosi aici ca sa determin radacinile din padure.
        std::copy(parent, parent + n, ancestors);

        for (int _ = 0; _ < max_log2; _++) {
            for (int i = 0; i < n; i++) {
                ancestorsNext[i] = ancestors[ancestors[i]];
            }

            swap(ancestors, ancestorsNext);
        }

        ///acum ancestors[i] indica radacina arborelui i din padure.

        for (int i = 0; i < m; i++) {
            if (ancestors[edges[i].a] != ancestors[edges[i].b]) {
                ///muchia mea e intre a si b. de fapt, in momentul de fata, muchia e intre bloburile lui a si b.
                ///eu trag muchia intre root[a] si root[b].

                workLeft = true; ///inca mai exista componente ce pot fi unite. s-ar putea sa nu le pot uni pe acestea 2 acum
                                 ///pentru ca ambele radacini sa indice "child".

                ///vreau ca una din radacini sa fie child si cealalta parent.
                if (orientation[ancestors[edges[i].a]] ^ orientation[ancestors[edges[i].b]]) {
                    if (!orientation[ancestors[edges[i].a]]) {
                        ///!nu e nevoie de lock pentru scriere. daca am un nod child cu mai multi vecini parent, nu ma
                        ///intereseaza cine reuseste sa scrie ultimul, vreau doar ca child sa indice catre cineva.
                        parent[ancestors[edges[i].a]] = ancestors[edges[i].b];
                    } else {
                        parent[ancestors[edges[i].b]] = ancestors[edges[i].a];
                    }
                }
            }
        }
    }

    int *isRoot = new int[n]();
    for (int i = 0; i < n; i++) {
        if (ancestors[i] == i) {
            isRoot[i] = 1;
        }
    }

    int p2 = 1;
    while ((p2 << 1) <= n) {
        p2 <<= 1;
    }

    int l = p2 - 1, r = n - 1;
    while (l) {
        for (int i = l; i <= r; i += 2) {
            isRoot[(i - 1) >> 1] += isRoot[i];
        }

        for (int i = l + 1; i <= r; i += 2) {
            isRoot[(i - 1) >> 1] += isRoot[i];
        }

        r = l - 1;
        l >>= 1;
    }

    fout << isRoot[0];

    delete[] isRoot;
    delete[] orientation;
    delete[] ancestorsNext;
    delete[] ancestors;
    delete[] parent;
    delete[] edges;

    fin.close();
    fout.close();

    return 0;
}