This documentation is automatically generated by online-judge-tools/verification-helper
#include "Graph/edgecoloring.hpp"
#pragma once #include "Graph/euler.hpp" struct EdgeColoring { typedef array<int, 2> P; typedef array<int, 3> T; int n; vector<P> _es, es; EdgeColoring() {} void add_edge(int u, int v) { _es.push_back({u, v}); } array<vector<int>, 2> divide(vector<P> tmp) { // divide eulerian trail EulerianTrail<0> euler(n * 2); for (auto &[x, y] : tmp) euler.add_edge(x, y); auto piece = euler.run().second; vector<int> p; for (auto &v : piece) p.insert(p.end(), ALL(v)); array<vector<int>, 2> res; rep(i, 0, p.size()) res[i & 1].push_back(p[i]); return res; } vector<int> find(vector<int> ids) { // find perfect matching int k = (int)ids.size() / n; assert(n * k == (int)ids.size()); int t = 0; while ((1 << t) < n * k) t++; int x = (1 << t) / k, y = (1 << t) % k; vector<int> ori(ids.size(), x), dum(n, y); while (t--) { vector<P> tmp; vector<int> add; int cnt = 0; rep(i, 0, ids.size()) { if (ori[i] & 1) { tmp.push_back({es[ids[i]][0], es[ids[i]][1]}); add.push_back(i); cnt++; } ori[i] >>= 1; } rep(i, 0, n) { if (dum[i] & 1) { tmp.push_back({i, n + i}); add.push_back(i); } dum[i] >>= 1; } array<vector<int>, 2> w = divide(tmp); int dum_cnt[2] = {}; rep(i, 0, 2) for (int x : w[i]) if (x >= cnt) dum_cnt[i]++; for (int i : w[dum_cnt[0] > dum_cnt[1]]) { if (i < cnt) ori[add[i]]++; else dum[add[i]]++; } } vector<int> res; rep(i, 0, ids.size()) if (ori[i]) res.push_back(ids[i]); assert((int)res.size() == n); return res; } vector<int> used; vector<vector<int>> colorize(vector<int> ids) { // must be regular bipartite graph int k = (int)ids.size() / n; assert(n * k == (int)ids.size()); if (k == 0) return {}; if (k == 1) return {ids}; vector<vector<int>> res; if (k & 1) { auto add = find(ids); for (int x : add) used[x] = 1; vector<int> rem; for (int x : ids) if (!used[x]) rem.push_back(x); for (int x : add) used[x] = 0; res = colorize(rem); res.push_back(add); } else { vector<P> tmp; for (int i : ids) tmp.push_back({es[i][0], es[i][1]}); array<vector<int>, 2> p = divide(tmp); rep(i, 0, 2) for (auto &x : p[i]) x = ids[x]; res = colorize(p[0]); int cur = k >> 1; while (__builtin_popcount(cur) != 1) { auto add = res.back(); res.pop_back(); p[1].insert(p[1].end(), ALL(add)); cur++; } auto add = colorize(p[1]); res.insert(res.end(), ALL(add)); } return res; } int run(vector<int> &res) { // normalize int LR[2] = {}; for (auto e : _es) rep(j, 0, 2) chmax(LR[j], e[j] + 1); vector<int> deg[2], kind[2], sz[2]; rep(i, 0, 2) deg[i].resize(LR[i]), kind[i].resize(LR[i]); for (auto e : _es) rep(j, 0, 2) deg[j][e[j]]++; int k = 0; rep(i, 0, 2) rep(j, 0, LR[i]) chmax(k, deg[i][j]); rep(j, 0, 2) { for (int i = 0; i < LR[j];) { int add = 0; while (i < LR[j] and deg[j][i] + add <= k) { kind[j][i] = (int)sz[j].size(); add += deg[j][i++]; } sz[j].push_back(add); } } rep(i, 0, 2) while (sz[i].size() < sz[i ^ 1].size()) sz[i].push_back(0); n = (int)sz[0].size(); for (auto e : _es) es.push_back({kind[0][e[0]], n + kind[1][e[1]]}); int nxt = 0; rep(i, 0, n) while (sz[0][i] < k) { while (sz[1][nxt] == k) nxt++; es.push_back({i, n + nxt}); sz[0][i]++; sz[1][nxt]++; } res.resize(_es.size()); used.resize(n * k, 0); vector<int> tmp(n * k); iota(ALL(tmp), 0); auto ret = colorize(tmp); rep(i, 0, ret.size()) for (int idx : ret[i]) { if (idx < (int)_es.size()) res[idx] = i; } return k; } }; /** * @brief Edge Coloring */
#line 2 "DataStructure/unionfind.hpp" struct UnionFind{ vector<int> par; int n; UnionFind(){} UnionFind(int _n):par(_n,-1),n(_n){} int root(int x){return par[x]<0?x:par[x]=root(par[x]);} bool same(int x,int y){return root(x)==root(y);} int size(int x){return -par[root(x)];} bool unite(int x,int y){ x=root(x),y=root(y); if(x==y)return false; if(size(x)>size(y))swap(x,y); par[y]+=par[x]; par[x]=y; n--; return true; } }; /** * @brief Union Find */ #line 3 "Graph/euler.hpp" template <bool directed> struct EulerianTrail { using P = pair<int, int>; int n, m; vector<vector<P>> g; UnionFind uni; vector<P> es; vector<int> used, deg; EulerianTrail() {} EulerianTrail(int N) : n(N), m(0), g(n), uni(n), deg(n) {} void add_edge(int u, int v) { es.push_back({u, v}); g[u].push_back({v, m}); uni.unite(u, v); if (directed) { deg[u]++; deg[v]--; } else { g[v].push_back({u, m}); deg[u]++; deg[v]++; } m++; } pair<vector<int>, vector<vector<int>>> run() { map<int, vector<int>> grps; rep(v, 0, n) grps[uni.root(v)].push_back(v); vector<int> start; vector<vector<int>> ret; used.assign(m, 0); for (auto &[_, vs] : grps) { int S = -1; if (directed) { for (auto &v : vs) { if (abs(deg[v]) > 1) return {}; else if (deg[v] == 1) { if (S == -1) S = v; else return {}; } } } else { int odd = 0; for (auto &v : vs) { if (deg[v] & 1) { S = v; odd++; } } if (odd > 2) return {}; } if (S == -1) S = vs.front(); auto add = get(S); if (add.size()) { start.push_back(S); ret.push_back(add); } } return {start, ret}; } private: vector<int> get(int s) { stack<P> st; vector<int> ret; st.push({s, -1}); while (!st.empty()) { int v = st.top().first; if (g[v].empty()) { ret.push_back(st.top().second); st.pop(); } else { auto &e = g[v].back(); g[v].pop_back(); if (used[e.second]) continue; used[e.second] = 1; st.push(e); } } ret.pop_back(); reverse(ALL(ret)); return ret; } }; /** * @brief Eulerian Trail */ #line 3 "Graph/edgecoloring.hpp" struct EdgeColoring { typedef array<int, 2> P; typedef array<int, 3> T; int n; vector<P> _es, es; EdgeColoring() {} void add_edge(int u, int v) { _es.push_back({u, v}); } array<vector<int>, 2> divide(vector<P> tmp) { // divide eulerian trail EulerianTrail<0> euler(n * 2); for (auto &[x, y] : tmp) euler.add_edge(x, y); auto piece = euler.run().second; vector<int> p; for (auto &v : piece) p.insert(p.end(), ALL(v)); array<vector<int>, 2> res; rep(i, 0, p.size()) res[i & 1].push_back(p[i]); return res; } vector<int> find(vector<int> ids) { // find perfect matching int k = (int)ids.size() / n; assert(n * k == (int)ids.size()); int t = 0; while ((1 << t) < n * k) t++; int x = (1 << t) / k, y = (1 << t) % k; vector<int> ori(ids.size(), x), dum(n, y); while (t--) { vector<P> tmp; vector<int> add; int cnt = 0; rep(i, 0, ids.size()) { if (ori[i] & 1) { tmp.push_back({es[ids[i]][0], es[ids[i]][1]}); add.push_back(i); cnt++; } ori[i] >>= 1; } rep(i, 0, n) { if (dum[i] & 1) { tmp.push_back({i, n + i}); add.push_back(i); } dum[i] >>= 1; } array<vector<int>, 2> w = divide(tmp); int dum_cnt[2] = {}; rep(i, 0, 2) for (int x : w[i]) if (x >= cnt) dum_cnt[i]++; for (int i : w[dum_cnt[0] > dum_cnt[1]]) { if (i < cnt) ori[add[i]]++; else dum[add[i]]++; } } vector<int> res; rep(i, 0, ids.size()) if (ori[i]) res.push_back(ids[i]); assert((int)res.size() == n); return res; } vector<int> used; vector<vector<int>> colorize(vector<int> ids) { // must be regular bipartite graph int k = (int)ids.size() / n; assert(n * k == (int)ids.size()); if (k == 0) return {}; if (k == 1) return {ids}; vector<vector<int>> res; if (k & 1) { auto add = find(ids); for (int x : add) used[x] = 1; vector<int> rem; for (int x : ids) if (!used[x]) rem.push_back(x); for (int x : add) used[x] = 0; res = colorize(rem); res.push_back(add); } else { vector<P> tmp; for (int i : ids) tmp.push_back({es[i][0], es[i][1]}); array<vector<int>, 2> p = divide(tmp); rep(i, 0, 2) for (auto &x : p[i]) x = ids[x]; res = colorize(p[0]); int cur = k >> 1; while (__builtin_popcount(cur) != 1) { auto add = res.back(); res.pop_back(); p[1].insert(p[1].end(), ALL(add)); cur++; } auto add = colorize(p[1]); res.insert(res.end(), ALL(add)); } return res; } int run(vector<int> &res) { // normalize int LR[2] = {}; for (auto e : _es) rep(j, 0, 2) chmax(LR[j], e[j] + 1); vector<int> deg[2], kind[2], sz[2]; rep(i, 0, 2) deg[i].resize(LR[i]), kind[i].resize(LR[i]); for (auto e : _es) rep(j, 0, 2) deg[j][e[j]]++; int k = 0; rep(i, 0, 2) rep(j, 0, LR[i]) chmax(k, deg[i][j]); rep(j, 0, 2) { for (int i = 0; i < LR[j];) { int add = 0; while (i < LR[j] and deg[j][i] + add <= k) { kind[j][i] = (int)sz[j].size(); add += deg[j][i++]; } sz[j].push_back(add); } } rep(i, 0, 2) while (sz[i].size() < sz[i ^ 1].size()) sz[i].push_back(0); n = (int)sz[0].size(); for (auto e : _es) es.push_back({kind[0][e[0]], n + kind[1][e[1]]}); int nxt = 0; rep(i, 0, n) while (sz[0][i] < k) { while (sz[1][nxt] == k) nxt++; es.push_back({i, n + nxt}); sz[0][i]++; sz[1][nxt]++; } res.resize(_es.size()); used.resize(n * k, 0); vector<int> tmp(n * k); iota(ALL(tmp), 0); auto ret = colorize(tmp); rep(i, 0, ret.size()) for (int idx : ret[i]) { if (idx < (int)_es.size()) res[idx] = i; } return k; } }; /** * @brief Edge Coloring */