topcoder srm 630 div1 (2-SAT and SCC template) [FINISHED]
problem1 link
首先计算任意两点的距离。然后枚举选出的集合中的两个点,判断其他点是否可以即可。
problem2 link
假设字符串为$s$,长度为$n$。后缀数组为$SA$,排名数组为$R$,即$R[SA_{i}]=i$那么对于连续的两个排名$SA_{i},SA_{i+1}$来说,应该尽量使得$s[SA_{i}]=s[SA_{i+1}]$。如果这个满足的话,那么需要两个后缀满足$R[SA_{i}+1]<R[SA_{i+1}+1]$
problem3 link
这个可以转化为2-SAT问题。
首先将每个点拆成202个点,分别表示$\leq 0,\leq 1,...,\leq 100,>0,>1,...,>100$.然后就是每一个等式可以转化为一些2-SAT中的限制。
比如对于$g_{x}+g_{y}<50$来说.比如$g_{x}> 19,g_{y}> 29$是冲突的,那么在2-SAT可以描述为$g_{x}> 19\rightarrow g_{y}\leq 29$,以及$g_{y}> 29\rightarrow g_{x}\leq 19$
最后就是得到2_SAT的一组解。
code for problem1
#include <algorithm> #include <unordered_set> #include <vector> class Egalitarianism3 { public: int maxCities(int n, const std::vector<int> &a, const std::vector<int> &b, const std::vector<int> &len) { if (n == 1) { return 1; } std::vector<std::vector<int>> g(n, std::vector<int>(n, -1)); for (int i = 0; i < n - 1; ++i) { g[a[i] - 1][b[i] - 1] = g[b[i] - 1][a[i] - 1] = len[i]; } for (int i = 0; i < n; ++i) { g[i][i] = 0; } for (int i = 0; i < n; ++i) { for (int u = 0; u < n; ++u) { if (g[u][i] != -1) { for (int v = 0; v < n; ++v) { if (g[i][v] != -1) { if (g[u][v] == -1 || g[u][v] > g[u][i] + g[i][v]) { g[u][v] = g[u][i] + g[i][v]; } } } } } } auto Compute = [&](int s, int t) { std::unordered_set<int> all; all.insert(s); all.insert(t); const int d = g[s][t]; for (int i = 0; i < n; ++i) { if (all.find(i) == all.end()) { bool tag = true; for (auto e : all) { if (g[i][e] != d) { tag = false; break; } } if (tag) { all.insert(i); } } } return static_cast<int>(all.size()); }; int result = 0; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { result = std::max(result, Compute(i, j)); } } return result; } };
code for problem2
#include <vector> class SuffixArrayDiv1 { public: int minimalCharacters(const std::vector<int> &SA) { int n = static_cast<int>(SA.size()); std::vector<int> s(n + 1, -1); for (int i = 0; i < n; ++i) { s[SA[i]] = i; } int result = 1; for (int i = 0; i + 1 < n; ++i) { if (s[SA[i] + 1] > s[SA[i + 1] + 1]) { ++result; } } return result; } };
code for problem3
#include <algorithm> #include <stack> #include <unordered_map> #include <unordered_set> #include <vector> class StronglyConnectedComponentSolver { public: StronglyConnectedComponentSolver() = default; void Initialize(int n) { edges_.resize(n); } std::vector<int> Solve() { total_ = static_cast<int>(edges_.size()); if (total_ == 0) { return {}; } visited_.resize(total_, false); low_indices_.resize(total_, 0); dfs_indices_.resize(total_, 0); connected_component_indices_.resize(total_, 0); for (int i = 0; i < total_; ++i) { if (0 == dfs_indices_[i]) { Dfs(i); } } return connected_component_indices_; } int VertexNumber() const { return static_cast<int>(edges_.size()); } inline void AddEdge(int from, int to) { edges_[from].push_back(to); } const std::vector<int> &Tos(int u) const { return edges_[u]; } private: void Dfs(const int u) { low_indices_[u] = dfs_indices_[u] = ++index_; stack_.push(u); visited_[u] = true; for (auto v : edges_[u]) { if (0 == dfs_indices_[v]) { Dfs(v); low_indices_[u] = std::min(low_indices_[u], low_indices_[v]); } else if (visited_[v]) { low_indices_[u] = std::min(low_indices_[u], dfs_indices_[v]); } } if (dfs_indices_[u] == low_indices_[u]) { int v = 0; do { v = stack_.top(); stack_.pop(); visited_[v] = false; connected_component_indices_[v] = connected_component_index_; } while (u != v); ++connected_component_index_; } } std::vector<std::vector<int>> edges_; int total_ = 0; std::vector<bool> visited_; std::vector<int> low_indices_; std::vector<int> dfs_indices_; std::stack<int> stack_; int index_ = 0; int connected_component_index_ = 0; std::vector<int> connected_component_indices_; }; class TwoSatisfiabilitySolver { public: void Initialize(int total_vertex_number) { scc_solver_.Initialize(total_vertex_number); } // If idx1 is type1, then idx2 must be type2. void AddConstraint(int idx1, bool type1, int idx2, bool type2) { int from = idx1 * 2 + (type1 ? 1 : 0); int to = idx2 * 2 + (type2 ? 1 : 0); scc_solver_.AddEdge(from, to); } void AddConflict(int idx1, bool type1, int idx2, bool type2) { AddConstraint(idx1, type1, idx2, !type2); AddConstraint(idx2, type2, idx1, !type1); } void AddLead(int idx1, bool type1, int idx2, bool type2) { AddConstraint(idx1, type1, idx2, type2); AddConstraint(idx2, !type2, idx1, !type1); } // The idx must not be type void SetFalse(int idx, bool type) { SetTrue(idx, !type); } // The idx must be type void SetTrue(int idx, bool type) { AddConstraint(idx, !type, idx, type); } bool ExistSolution() { if (scc_indices_.empty()) { scc_indices_ = scc_solver_.Solve(); total_scc_number_ = *std::max_element(scc_indices_.begin(), scc_indices_.end()) + 1; } for (int i = 0; i < scc_solver_.VertexNumber() / 2; ++i) { if (scc_indices_[i * 2] == scc_indices_[i * 2 + 1]) { return false; } } return true; } std::vector<bool> GetOneSolution() { if (!ExistSolution()) { return {}; } BuildNewGraph(); TopSort(); int total = scc_solver_.VertexNumber(); std::vector<bool> result(total / 2); for (int e = 0; e < total / 2; ++e) { if (last_color_[scc_indices_[e * 2]] == 0) { result[e] = false; } else { result[e] = true; } } return std::move(result); } private: void BuildNewGraph() { new_edges_.resize(total_scc_number_); new_graph_node_in_degree_.resize(total_scc_number_, 0); int total = scc_solver_.VertexNumber(); for (int i = 0; i < total; ++i) { int scc0 = scc_indices_[i]; for (auto e : scc_solver_.Tos(i)) { int scc1 = scc_indices_[e]; if (scc0 != scc1 && new_edges_[scc1].find(scc0) == new_edges_[scc1].end()) { new_edges_[scc1].insert(scc0); ++new_graph_node_in_degree_[scc0]; } } } } void TopSort() { std::vector<int> conflict(total_scc_number_); int total = scc_solver_.VertexNumber() / 2; for (int i = 0; i < total; ++i) { conflict[scc_indices_[i * 2]] = scc_indices_[i * 2 + 1]; conflict[scc_indices_[i * 2 + 1]] = scc_indices_[i * 2]; } last_color_.resize(total_scc_number_, -1); std::stack<int> st; for (int i = 0; i < total_scc_number_; ++i) { if (0 == new_graph_node_in_degree_[i]) { st.push(i); } } while (!st.empty()) { int u = st.top(); st.pop(); if (last_color_[u] == -1) { last_color_[u] = 0; last_color_[conflict[u]] = 1; } for (auto e : new_edges_[u]) { int cur = --new_graph_node_in_degree_[e]; if (cur == 0) { st.push(e); } } } } std::vector<int> scc_indices_; int total_scc_number_ = 0; std::vector<std::unordered_set<int>> new_edges_; std::vector<int> new_graph_node_in_degree_; std::vector<int> last_color_; StronglyConnectedComponentSolver scc_solver_; }; class NeverAskHerAge { public: std::vector<int> possibleSolution(int n, const std::vector<int> &id1, const std::vector<int> &id2, const std::vector<std::string> &op, const std::vector<std::string> &rl, const std::vector<int> &val) { solver.Initialize(n * 101 * 2); int m = static_cast<int>(id1.size()); // 0: <= j // 1: > j for (int i = 1; i <= n; ++i) { for (int j = 0; j <= 100; ++j) { if (j > 0) { solver.AddLead(GetIndex(i, j), 1, GetIndex(i, j - 1), 1); } if (j + 1 < 100) { solver.AddLead(GetIndex(i, j), 0, GetIndex(i, j + 1), 0); } } solver.SetFalse(GetIndex(i, 0), 0); solver.SetFalse(GetIndex(i, 100), 1); } for (int i = 0; i < m; ++i) { if (rl[i] == "=") { Add(id1[i], id2[i], op[i][0], ">=", val[i]); Add(id1[i], id2[i], op[i][0], "<=", val[i]); } else { Add(id1[i], id2[i], op[i][0], rl[i], val[i]); } } if (!solver.ExistSolution()) { return {}; } std::vector<int> result(n); auto sol = solver.GetOneSolution(); for (int i = 0; i < n; ++i) { for (int j = 1; j < 101; ++j) { int t = GetIndex(i + 1, j); if (!sol[t]) { result[i] = j; break; } } } return result; } private: void Add(int x, int y, char op, const std::string &rl, int z) { if (op == '+' || op == '*') { if (rl[0] == '<') { AddMulLess(x, y, op, rl, z); } else { AddMulGreater(x, y, op, rl, z); } } else { if (rl[0] == '<') { SubDivLess(x, y, op, rl, z); } else { SubDivGreater(x, y, op, rl, z); } } } void AddMulLess(int g1, int g2, char op, const std::string &rl, int z) { // Assume g2 > i - 1, (i, i+1, i+2, ..., 100) for (int i = 1; i <= 101; ++i) { // If rl is '<', then 1000(i + j) < z. // Here consider opposite: 1000(i + j) >= z. // Get j >= ceil((z - 1000i) / 1000) = (z - 1000i + 999) / 1000 // So g2 >= i and g1 >= j conflicts // If rl is '<=', then 1000(i + j) <= z. // Here consider opposite: 1000(i + j) > z. // Get j >= floor((z - 1000i) / 1000) + 1 = (z - 1000i + 1000) / 1000 // So g2 >= i and g1 >= j conflicts int j = op == '+' ? Ceil(z - 1000 * i, 1000, EqualTag(rl)) : Ceil(z, i * 1000, EqualTag(rl)); if (j < 1) { solver.SetFalse(GetIndex(g2, i - 1), 1); } else if (j <= 101) { solver.AddConflict(GetIndex(g2, i - 1), 1, GetIndex(g1, j - 1), 1); } } } void AddMulGreater(int g1, int g2, char op, const std::string &rl, int z) { for (int i = 0; i < 101; ++i) { int j = op == '+' ? Floor(z - 1000 * i, 1000, EqualTag(rl)) : Floor(z, i * 1000, EqualTag(rl)); if (j >= 101) { solver.SetFalse(GetIndex(g2, i), 0); } else if (j >= 0) { solver.AddConflict(GetIndex(g2, i), 0, GetIndex(g1, j), 0); } } } void SubDivGreater(int g1, int g2, char op, const std::string &rl, int z) { for (int i = 1; i <= 101; ++i) { int j = op == '-' ? Floor(z + 1000 * i, 1000, EqualTag(rl)) : Floor(z * i, 1000, EqualTag(rl)); if (j >= 101) { solver.SetFalse(GetIndex(g2, i - 1), 1); } else if (j >= 0) { solver.AddConflict(GetIndex(g2, i - 1), 1, GetIndex(g1, j), 0); } } } void SubDivLess(int g1, int g2, char op, const std::string &rl, int z) { for (int i = 0; i < 101; ++i) { int j = op == '-' ? Ceil(z + 1000 * i, 1000, EqualTag(rl)) : Ceil(z * i, 1000, EqualTag(rl)); if (j < 1) { solver.SetFalse(GetIndex(g2, i), 0); } else if (j <= 101) { solver.AddConflict(GetIndex(g2, i), 0, GetIndex(g1, j - 1), 1); } } } bool EqualTag(const std::string &rl) { return rl.length() < 2; } int Ceil(int x, int y, bool tag) { if (x < 0) { return -1; } return (x + y - (tag ? 1 : 0)) / y; } int Floor(int x, int y, bool tag) { if (y == 0) { return 101; } if (x <= 0) { return -1; } return (x - (tag ? 0 : 1)) / y; } int GetIndex(int i, int j) { return (i - 1) * 101 + j; } TwoSatisfiabilitySolver solver; };