From 6ee1fb4671b4c8236eef746dd55fcf47a084adab Mon Sep 17 00:00:00 2001 From: Horace He Date: Wed, 15 Apr 2020 19:39:13 -0400 Subject: [PATCH 1/3] Added first draft of matroid intersection --- content/graph/MatroidIntersection.h | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 content/graph/MatroidIntersection.h diff --git a/content/graph/MatroidIntersection.h b/content/graph/MatroidIntersection.h new file mode 100644 index 000000000..01618f862 --- /dev/null +++ b/content/graph/MatroidIntersection.h @@ -0,0 +1,55 @@ +/** + * Author: tfg, Aeren, pajenegod, chilli + * Date: 2020-04-15 + * License: CC0 + * Source: + * Description: + * Time: + * Status: + */ +template struct MatroidIsect { + int n; + vector gset; vector iset; + vector m1; vector m2; + MatroidIsect(M1 m1, M2 m2, vector gset) : + n(sz(gset)),gset(gset), iset(n+1), m1(n+1,m1), m2(n+1,m2) {} + vector solve() { + iset[n] = true; // Add greedily + rep(i,0,n) if (test(m1, i, n) && test(m2, i, n)) { + iset[i] = true; + m1[n].insert(gset[i]), m2[n].insert(gset[i]); + } + while (augment()); + vector ans; + rep(i,0,n) if (iset[i]) ans.push_back(gset[i]); + return ans; + } + template + bool test(vector &m, int add, int rem) { + return !iset[add] && iset[rem] && m[rem].test(gset[add]); + } + bool augment() { + rep(i,0,n + 1) { // Construct matroids + if (iset[i]) { + m1[i].clear(), m2[i].clear(); + rep(v, 0, n) if (v != i && iset[v]) + m1[i].add(gset[v]), m2[i].add(gset[v]); + } + } + queue q({n}); + vector frm(n + 1, -1); + while (!q.empty()) { // Augment path + int on = q.front(); q.pop(); + rep(v,0,n+1) if (frm[v] == -1 && + (test(m1, v, on) || test(m2, on, v))) { + if (v == n) { + while (on != n) + iset[on] = !iset[on], on = frm[on]; + return true; + } + q.push(v), frm[v] = on; + } + } + return false; + } +}; \ No newline at end of file From 8e7d8ffade34c0cd06530e118d3e497653f0bfe7 Mon Sep 17 00:00:00 2001 From: Horace He Date: Wed, 15 Apr 2020 19:47:10 -0400 Subject: [PATCH 2/3] Added WIP matroid code --- content/graph/MatroidIntersection.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/content/graph/MatroidIntersection.h b/content/graph/MatroidIntersection.h index 01618f862..e16369456 100644 --- a/content/graph/MatroidIntersection.h +++ b/content/graph/MatroidIntersection.h @@ -7,6 +7,11 @@ * Time: * Status: */ +struct Matroid { + bool check(int x) {} // will adding x make matroid dependent + void add(int x) {} // adds x to matroid + void clear() {} // resets matroid to empty +}; template struct MatroidIsect { int n; vector gset; vector iset; @@ -26,7 +31,7 @@ template struct MatroidIsect { } template bool test(vector &m, int add, int rem) { - return !iset[add] && iset[rem] && m[rem].test(gset[add]); + return !iset[add] && iset[rem] && m[rem].check(gset[add]); } bool augment() { rep(i,0,n + 1) { // Construct matroids From 660f91ce3f29f0e212229c56f4dd39f4c15b2150 Mon Sep 17 00:00:00 2001 From: Horace He Date: Thu, 16 Apr 2020 18:21:12 -0400 Subject: [PATCH 3/3] Added Matroid Intersection --- content/graph/MatroidIntersection.h | 109 +++++++++++++++++----------- content/graph/chapter.tex | 3 + 2 files changed, 68 insertions(+), 44 deletions(-) diff --git a/content/graph/MatroidIntersection.h b/content/graph/MatroidIntersection.h index e16369456..99c7fe2cd 100644 --- a/content/graph/MatroidIntersection.h +++ b/content/graph/MatroidIntersection.h @@ -2,59 +2,80 @@ * Author: tfg, Aeren, pajenegod, chilli * Date: 2020-04-15 * License: CC0 - * Source: - * Description: - * Time: - * Status: + * Source: https://codeforces.com/blog/entry/69287 + * Description: Given two matroids, finds the largest common independent set. + * For the color and graph matroids, this would be the largest forest where no + * two edges are the same color. A matroid has 3 functions + * - check(int x): returns if current matroid can add x without becoming dependent + * - add(int x): adds an element to the matroid (guaranteed to never make it dependent) + * - clear(): sets the matroid to the empty matroid + * The matroid is given an int representing the element, and is expected to + * convert it (e.g: the color or the endpoints) + * Pass the matroid with more expensive add/clear operations to M1. + * Time: R^2N(M2.add + M1.check + M2.check) + R^3 M1.add + R^2 M1.clear + RN M2.clear + * Status: Tested on SWERC 2011D, Pick Your Own Nim + * Usage: + * Details: */ -struct Matroid { - bool check(int x) {} // will adding x make matroid dependent - void add(int x) {} // adds x to matroid - void clear() {} // resets matroid to empty +#include "../data-structures/UnionFind.h" + +struct ColorMat { + vi cnt, clr; + ColorMat(int n, vector clr) : cnt(n), clr(clr) {} + bool check(int x) { return !cnt[clr[x]]; } + void add(int x) { cnt[clr[x]]++; } + void clear() { fill(all(cnt), 0); } }; -template struct MatroidIsect { +struct GraphMat { + UF uf; + vector> e; + GraphMat(int n, vector> e) : uf(n), e(e) {} + bool check(int x) { return !uf.sameSet(e[x][0], e[x][1]); } + void add(int x) { uf.join(e[x][0], e[x][1]); } + void clear() { uf = UF(sz(uf.e)); } +}; +template struct MatroidIsect { int n; - vector gset; vector iset; - vector m1; vector m2; - MatroidIsect(M1 m1, M2 m2, vector gset) : - n(sz(gset)),gset(gset), iset(n+1), m1(n+1,m1), m2(n+1,m2) {} - vector solve() { - iset[n] = true; // Add greedily - rep(i,0,n) if (test(m1, i, n) && test(m2, i, n)) { - iset[i] = true; - m1[n].insert(gset[i]), m2[n].insert(gset[i]); - } + vector iset; + M1 m1; M2 m2; + MatroidIsect(M1 m1, M2 m2, int n) : n(n), iset(n + 1), m1(m1), m2(m2) {} + vi solve() { + rep(i,0,n) if (m1.check(i) && m2.check(i)) + iset[i] = true, m1.add(i), m2.add(i); while (augment()); - vector ans; - rep(i,0,n) if (iset[i]) ans.push_back(gset[i]); + vi ans; + rep(i,0,n) if (iset[i]) ans.push_back(i); return ans; } - template - bool test(vector &m, int add, int rem) { - return !iset[add] && iset[rem] && m[rem].check(gset[add]); - } bool augment() { - rep(i,0,n + 1) { // Construct matroids - if (iset[i]) { - m1[i].clear(), m2[i].clear(); - rep(v, 0, n) if (v != i && iset[v]) - m1[i].add(gset[v]), m2[i].add(gset[v]); - } - } - queue q({n}); - vector frm(n + 1, -1); - while (!q.empty()) { // Augment path - int on = q.front(); q.pop(); - rep(v,0,n+1) if (frm[v] == -1 && - (test(m1, v, on) || test(m2, on, v))) { - if (v == n) { - while (on != n) - iset[on] = !iset[on], on = frm[on]; + vector frm(n, -1); + queue q({n}); // starts at dummy node + auto fwdE = [&](int a) { + vi ans; + m1.clear(); + rep(v, 0, n) if (iset[v] && v != a) m1.add(v); + rep(b, 0, n) if (!iset[b] && frm[b] == -1 && m1.check(b)) + ans.push_back(b), frm[b] = a; + return ans; + }; + auto backE = [&](int b) { + m2.clear(); + rep(cas, 0, 2) rep(v, 0, n) + if ((v == b || iset[v]) && (frm[v] == -1) == cas) { + if (!m2.check(v)) + return cas ? q.push(v), frm[v] = b, v : -1; + m2.add(v); + } + return n; + }; + while (!q.empty()) { + int a = q.front(), c; q.pop(); + for (int b : fwdE(a)) + while((c = backE(b)) >= 0) if (c == n) { + while (b != n) iset[b] ^= 1, b = frm[b]; return true; } - q.push(v), frm[v] = on; - } } return false; } -}; \ No newline at end of file +}; diff --git a/content/graph/chapter.tex b/content/graph/chapter.tex index be939f15a..af06499a8 100644 --- a/content/graph/chapter.tex +++ b/content/graph/chapter.tex @@ -16,6 +16,9 @@ \section{Network flow} \kactlimport{MinCut.h} \kactlimport{GlobalMinCut.h} +\section{Matroids} + \kactlimport{MatroidIntersection.h} + \section{Matching} \kactlimport{hopcroftKarp.h} \kactlimport{DFSMatching.h}