noya2_Library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub noya2ruler/noya2_Library

:heavy_check_mark: graph/cycle_detection.hpp

Depends on

Verified with

Code

#pragma once

#include <optional>
#include <vector>
#include <utility>
#include <algorithm>

#include"data_structure/csr.hpp"

namespace noya2 {

std::optional<std::vector<int>> cycle_detection_directed(int n, const std::vector<std::pair<int,int>> &es){
    internal::csr<std::pair<int,int>> g(n,es.size());
    for (int i = 0; auto [u, v] : es){
        g.add(u,std::pair<int,int>(v, i));
        i++;
    }
    g.build();
    std::vector<bool> seen(n,false), done(n,false);
    std::vector<int> cycle;
    // -1:over,-2:done
    auto dfs = [&](auto sfs, int v, int pid) -> int {
        if (seen[v]) return v;
        if (done[v]) return -1;
        seen[v] = true;
        for (auto &[to, eid] : g[v]) if (eid != pid){
            int nxt = sfs(sfs, to, eid);
            if (nxt != -1){
                if (nxt == -2) return -2;
                cycle.emplace_back(eid);
                if (nxt == v) return -2;
                return nxt;
            }
        }
        seen[v] = false;
        done[v] = true;
        return -1;
    };
    for (int i = 0; i < n; i++){
        if (dfs(dfs, i, -1) == -2){
            std::reverse(cycle.begin(), cycle.end());
            return cycle;
        }
    }
    return std::nullopt;
}

} // namespace noya2
#line 2 "graph/cycle_detection.hpp"

#include <optional>
#include <vector>
#include <utility>
#include <algorithm>

#line 2 "data_structure/csr.hpp"

#line 4 "data_structure/csr.hpp"
#include<ranges>
#include<cassert>
#line 7 "data_structure/csr.hpp"

namespace noya2::internal {

template<class E>
struct csr {
    csr () {}
    csr (int _n) : n(_n) {}
    csr (int _n, int m) : n(_n){
        start.reserve(m);
        elist.reserve(m);
    }
    // ACL style constructor (do not have to call build)
    csr (int _n, const std::vector<std::pair<int,E>> &idx_elem) : n(_n), start(_n + 2), elist(idx_elem.size()) {
        for (auto &[i, e] : idx_elem){
            start[i + 2]++;
        }
        for (int i = 1; i < n; i++){
            start[i + 2] += start[i + 1];
        }
        for (auto &[i, e] : idx_elem){
            elist[start[i + 1]++] = e;
        }
        prepared = true;
    }
    int add(int idx, E elem){
        int eid = start.size();
        start.emplace_back(idx);
        elist.emplace_back(elem);
        return eid;
    }
    void build(){
        if (prepared) return ;
        int m = start.size();
        std::vector<E> nelist(m);
        std::vector<int> nstart(n + 2, 0);
        for (int i = 0; i < m; i++){
            nstart[start[i] + 2]++;
        }
        for (int i = 1; i < n; i++){
            nstart[i + 2] += nstart[i + 1];
        }
        for (int i = 0; i < m; i++){
            nelist[nstart[start[i] + 1]++] = elist[i];
        }
        swap(elist,nelist);
        swap(start,nstart);
        prepared = true;
    }
    const auto operator[](int idx) const {
        return std::ranges::subrange(elist.begin()+start[idx],elist.begin()+start[idx+1]);
    }
    auto operator[](int idx){
        return std::ranges::subrange(elist.begin()+start[idx],elist.begin()+start[idx+1]);
    }
    const auto operator()(int idx, int l, int r) const {
        return std::ranges::subrange(elist.begin()+start[idx]+l,elist.begin()+start[idx]+r);
    }
    auto operator()(int idx, int l, int r){
        return std::ranges::subrange(elist.begin()+start[idx]+l,elist.begin()+start[idx]+r);
    }
    size_t size() const {
        return n;
    }
    int n;
    std::vector<int> start;
    std::vector<E> elist;
    bool prepared = false;
};

} // namespace noya2::internal
#line 9 "graph/cycle_detection.hpp"

namespace noya2 {

std::optional<std::vector<int>> cycle_detection_directed(int n, const std::vector<std::pair<int,int>> &es){
    internal::csr<std::pair<int,int>> g(n,es.size());
    for (int i = 0; auto [u, v] : es){
        g.add(u,std::pair<int,int>(v, i));
        i++;
    }
    g.build();
    std::vector<bool> seen(n,false), done(n,false);
    std::vector<int> cycle;
    // -1:over,-2:done
    auto dfs = [&](auto sfs, int v, int pid) -> int {
        if (seen[v]) return v;
        if (done[v]) return -1;
        seen[v] = true;
        for (auto &[to, eid] : g[v]) if (eid != pid){
            int nxt = sfs(sfs, to, eid);
            if (nxt != -1){
                if (nxt == -2) return -2;
                cycle.emplace_back(eid);
                if (nxt == v) return -2;
                return nxt;
            }
        }
        seen[v] = false;
        done[v] = true;
        return -1;
    };
    for (int i = 0; i < n; i++){
        if (dfs(dfs, i, -1) == -2){
            std::reverse(cycle.begin(), cycle.end());
            return cycle;
        }
    }
    return std::nullopt;
}

} // namespace noya2
Back to top page