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
*/