Sobre

Matroids de Grafo e Particao

De modo geral, toda Matroid contem um build() linear

e uma funcao constante oracle()

oracle(i) responde se o conjunto continua independente

apos adicao do elemento i

oracle(i, j) responde se o conjunto continua indepente

apos trocar o elemento i pelo elemento j

Intersecao sem peso O(r^2 n)

em que n eh o tamanho do conjunto e r eh o tamanho da resposta

Matroid Grafica

Matroid das florestas de um grafo

Um conjunto de arestas eh independente se formam uma floresta

build() : O(n)

oracle() : O(1)

Link original: matroid.cpp

Código

  struct graphic_matroid {
	int n, m, t;
	vector<array<int, 2>> edges;
	vector<vector<int>> g;
	vector<int> comp, in, out;
	graphic_matroid(int n_, vector<array<int, 2>> edges_) 
		: n(n_), m(edges_.size()), edges(edges_), g(n), comp(n), in(n), out(n) {}
	void dfs(int u) {
		in[u] = t++;
		for (auto v : g[u]) if (in[v] == -1)
			comp[v] = comp[u], dfs(v);
		out[u] = t;
	}
	void build(vector<int> I) {
		t = 0;
		for (int u = 0; u < n; u++) g[u].clear(), in[u] = -1;
		for (int e : I) {
			auto [u, v] = edges[e];
			g[u].push_back(v), g[v].push_back(u);
		}
		for (int u = 0; u < n; u++) if (in[u] == -1)
			comp[u] = u, dfs(u);
	}
	bool is_ancestor(int u, int v) {
		return in[u] <= in[v] and in[v] < out[u];
	}
	bool oracle(int e) {
		return comp[edges[e][0]] != comp[edges[e][1]];
	}
	bool oracle(int e, int f) {
		if (oracle(f)) return true;
		int u = edges[e][in[edges[e][0]] < in[edges[e][1]]];
		return is_ancestor(u, edges[f][0]) != is_ancestor(u, edges[f][1]);
	}
};

// Matroid de particao ou cores
// Um conjunto eh independente se a quantidade de elementos
// de cada cor nao excede a capacidade da cor
// Quando todas as capacidades sao 1, um conjunto eh independente
// se todas as suas cores sao distintas
// 
// build() : O(n)
// oracle() : O(1)

struct partition_matroid {
	vector<int> cap, color, d;
	partition_matroid(vector<int> cap_, vector<int> color_) 
		: cap(cap_), color(color_), d(cap.size()) {}
	void build(vector<int> I) {
		fill(d.begin(), d.end(), 0);
		for (int u : I) d[color[u]]++;
	}
	bool oracle(int u) {
		return d[color[u]] < cap[color[u]];
	}
	bool oracle(int u, int v) {
		return color[u] == color[v] or oracle(v);
	}
};

// Intersecao de matroid sem pesos
// Dadas duas matroids M1 e M2 definidas sobre o mesmo
// conjunto I, retorna o maior subconjunto de I
// que eh independente tanto para M1 quanto para M2
//
// O(r^2*n)

// Matroid "pesada" deve ser a M2
template<typename Matroid1, typename Matroid2>
vector<int> matroid_intersection(int n, Matroid1 M1, Matroid2 M2) {
	vector<bool> b(n);
	vector<int> I[2];
	bool converged = false;
	while (!converged) {
		I[0].clear(), I[1].clear();
		for (int u = 0; u < n; u++) I[b[u]].push_back(u);

		M1.build(I[1]), M2.build(I[1]);
		vector<bool> target(n), pushed(n);
		queue<int> q;
		for (int u : I[0]) {
			target[u] = M2.oracle(u);
			if (M1.oracle(u)) pushed[u] = true, q.push(u);
		}
		vector<int> p(n, -1);
		converged = true;
		while (q.size()) {
			int u = q.front(); q.pop();
			if (target[u]) {
				converged = false;
				for (int v = u; v != -1; v = p[v]) b[v] = !b[v];
				break;
			}
			for (int v : I[!b[u]]) if (!pushed[v]) {
				if ((b[u] and M1.oracle(u, v)) or (b[v] and M2.oracle(v, u)))
					p[v] = u, pushed[v] = true, q.push(v);
			}
		}
	}
	return I[1];
}

// Intersecao de matroid com pesos
// Dadas duas matroids M1 e M2 e uma funcao de pesos w, todas definidas sobre
// um conjunto I retorna o maior subconjunto de I (desempatado pelo menor peso)
// que eh independente tanto para M1 quanto para M2
// A resposta eh construida incrementando o tamanho conjunto I de 1 em 1
// Se nao tiver custo negativo, nao precisa de SPFA
//
// O(r^3*n) com SPFA
// O(r^2*n*log(n)) com Dijkstra e potencial

template<typename T, typename Matroid1, typename Matroid2>
vector<int> weighted_matroid_intersection(int n, vector<T> w, Matroid1 M1, Matroid2 M2) {
	vector<bool> b(n), target(n), is_inside(n);
	vector<int> I[2], from(n);
	vector<pair<T, int>> d(n);
	auto check_edge = [&](int u, int v) {
		return (b[u] and M1.oracle(u, v)) or (b[v] and M2.oracle(v, u));
	};
	while (true) {
		I[0].clear(), I[1].clear();
		for (int u = 0; u < n; u++) I[b[u]].push_back(u);
		// I[1] contem o conjunto de tamanho I[1].size() de menor peso
		M1.build(I[1]), M2.build(I[1]);
		for (int u = 0; u < n; u++) {
			target[u] = false, is_inside[u] = false, from[u] = -1;
			d[u] = {numeric_limits<T>::max(), INF};
		}
		deque<T> q;
		sort(I[0].begin(), I[0].end(), [&](int i, int j){ return w[i] < w[j]; });
		for (int u : I[0]) {
			target[u] = M2.oracle(u);
			if (M1.oracle(u)) {
				if (is_inside[u]) continue;
				d[u] = {w[u], 0};
				if (!q.empty() and d[u] > d[q.front()]) q.push_back(u);
				else q.push_front(u);
				is_inside[u] = true;
			}
		}	
		while (q.size()) {
			int u = q.front(); q.pop_front();
			is_inside[u] = false;
			for (int v : I[!b[u]]) if (check_edge(u, v)) {
				pair<T, int> nd(d[u].first + w[v], d[u].second + 1);
				if (nd < d[v]) {
					from[v] = u, d[v] = nd;
					if (is_inside[v]) continue;
					if (q.size() and d[v] > d[q.front()]) q.push_back(v);
					else q.push_front(v);
					is_inside[v] = true;
				}
			}
		}
		pair<T, int> mini = pair(numeric_limits<T>::max(), INF);
		int targ = -1;
		for (int u : I[0]) if (target[u] and d[u] < mini) 
			mini = d[u], targ = u;	
		if (targ != -1) for (int u = targ; u != -1; u = from[u])
			b[u] = !b[u], w[u] *= -1;
		else break;
	}
	return I[1];
}