#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using lint = long long; using pint = pair; using plint = pair; struct fast_ios { fast_ios(){ cin.tie(nullptr), ios::sync_with_stdio(false), cout << fixed << setprecision(20); }; } fast_ios_; #define ALL(x) (x).begin(), (x).end() #define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i=i##_begin_;i--) #define REP(i, n) FOR(i,0,n) #define IREP(i, n) IFOR(i,0,n) template void ndarray(vector& vec, const V& val, int len) { vec.assign(len, val); } template void ndarray(vector& vec, const V& val, int len, Args... args) { vec.resize(len), for_each(begin(vec), end(vec), [&](T& v) { ndarray(v, val, args...); }); } template bool chmax(T &m, const T q) { return m < q ? (m = q, true) : false; } template bool chmin(T &m, const T q) { return m > q ? (m = q, true) : false; } const std::vector> grid_dxs{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; int floor_lg(long long x) { return x <= 0 ? -1 : 63 - __builtin_clzll(x); } template T1 floor_div(T1 num, T2 den) { return (num > 0 ? num / den : -((-num + den - 1) / den)); } template std::pair operator+(const std::pair &l, const std::pair &r) { return std::make_pair(l.first + r.first, l.second + r.second); } template std::pair operator-(const std::pair &l, const std::pair &r) { return std::make_pair(l.first - r.first, l.second - r.second); } template std::vector sort_unique(std::vector vec) { sort(vec.begin(), vec.end()), vec.erase(unique(vec.begin(), vec.end()), vec.end()); return vec; } template int arglb(const std::vector &v, const T &x) { return std::distance(v.begin(), std::lower_bound(v.begin(), v.end(), x)); } template int argub(const std::vector &v, const T &x) { return std::distance(v.begin(), std::upper_bound(v.begin(), v.end(), x)); } template IStream &operator>>(IStream &is, std::vector &vec) { for (auto &v : vec) is >> v; return is; } template OStream &operator<<(OStream &os, const std::vector &vec); template OStream &operator<<(OStream &os, const std::array &arr); template OStream &operator<<(OStream &os, const std::unordered_set &vec); template OStream &operator<<(OStream &os, const pair &pa); template OStream &operator<<(OStream &os, const std::deque &vec); template OStream &operator<<(OStream &os, const std::set &vec); template OStream &operator<<(OStream &os, const std::multiset &vec); template OStream &operator<<(OStream &os, const std::unordered_multiset &vec); template OStream &operator<<(OStream &os, const std::pair &pa); template OStream &operator<<(OStream &os, const std::map &mp); template OStream &operator<<(OStream &os, const std::unordered_map &mp); template OStream &operator<<(OStream &os, const std::tuple &tpl); template OStream &operator<<(OStream &os, const std::vector &vec) { os << '['; for (auto v : vec) os << v << ','; os << ']'; return os; } template OStream &operator<<(OStream &os, const std::array &arr) { os << '['; for (auto v : arr) os << v << ','; os << ']'; return os; } #if __cplusplus >= 201703L template std::istream &operator>>(std::istream &is, std::tuple &tpl) { std::apply([&is](auto &&... args) { ((is >> args), ...);}, tpl); return is; } template OStream &operator<<(OStream &os, const std::tuple &tpl) { os << '('; std::apply([&os](auto &&... args) { ((os << args << ','), ...);}, tpl); return os << ')'; } #endif template OStream &operator<<(OStream &os, const std::unordered_set &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; } template OStream &operator<<(OStream &os, const std::deque &vec) { os << "deq["; for (auto v : vec) os << v << ','; os << ']'; return os; } template OStream &operator<<(OStream &os, const std::set &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; } template OStream &operator<<(OStream &os, const std::multiset &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; } template OStream &operator<<(OStream &os, const std::unordered_multiset &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; } template OStream &operator<<(OStream &os, const std::pair &pa) { return os << '(' << pa.first << ',' << pa.second << ')'; } template OStream &operator<<(OStream &os, const std::map &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; } template OStream &operator<<(OStream &os, const std::unordered_map &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; } #ifdef HITONANODE_LOCAL const string COLOR_RESET = "\033[0m", BRIGHT_GREEN = "\033[1;32m", BRIGHT_RED = "\033[1;31m", BRIGHT_CYAN = "\033[1;36m", NORMAL_CROSSED = "\033[0;9;37m", RED_BACKGROUND = "\033[1;41m", NORMAL_FAINT = "\033[0;2m"; #define dbg(x) std::cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << std::endl #define dbgif(cond, x) ((cond) ? std::cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << std::endl : std::cerr) #else #define dbg(x) ((void)0) #define dbgif(cond, x) ((void)0) #endif // Cost scaling // https://people.orie.cornell.edu/dpw/orie633/ template struct mcf_costscaling { mcf_costscaling() = default; mcf_costscaling(int n) : _n(n), to(n), b(n), p(n) {} int _n; std::vector cap; std::vector cost; std::vector opposite; std::vector> to; std::vector b; std::vector p; int add_edge(int from_, int to_, Cap cap_, Cost cost_) { assert(0 <= from_ and from_ < _n); assert(0 <= to_ and to_ < _n); assert(0 <= cap_); cost_ *= (_n + 1); const int e = int(cap.size()); to[from_].push_back(e); cap.push_back(cap_); cost.push_back(cost_); opposite.push_back(to_); to[to_].push_back(e + 1); cap.push_back(0); cost.push_back(-cost_); opposite.push_back(from_); return e / 2; } void add_supply(int v, Cap supply) { b[v] += supply; } void add_demand(int v, Cap demand) { add_supply(v, -demand); } template RetCost solve() { Cost eps = 1; std::vector que; for (const auto c : cost) { while (eps <= -c) eps <<= SCALING; } for (; eps >>= SCALING;) { auto no_admissible_cycle = [&]() -> bool { for (int i = 0; i < _n; i++) { if (b[i]) return false; } std::vector pp = p; for (int iter = 0; iter < REFINEMENT_ITER; iter++) { bool flg = false; for (int e = 0; e < int(cap.size()); e++) { if (!cap[e]) continue; int i = opposite[e ^ 1], j = opposite[e]; if (pp[j] > pp[i] + cost[e] + eps) pp[j] = pp[i] + cost[e] + eps, flg = true; } if (!flg) return p = pp, true; } return false; }; if (no_admissible_cycle()) continue; // Refine for (int e = 0; e < int(cap.size()); e++) { const int i = opposite[e ^ 1], j = opposite[e]; const Cost cp_ij = cost[e] + p[i] - p[j]; if (cap[e] and cp_ij < 0) b[i] -= cap[e], b[j] += cap[e], cap[e ^ 1] += cap[e], cap[e] = 0; } que.clear(); int qh = 0; for (int i = 0; i < _n; i++) { if (b[i] > 0) que.push_back(i); } std::vector iters(_n); while (qh < int(que.size())) { const int i = que[qh++]; for (; iters[i] < int(to[i].size()) and b[i]; ++iters[i]) { // Push int e = to[i][iters[i]]; if (!cap[e]) continue; int j = opposite[e]; Cost cp_ij = cost[e] + p[i] - p[j]; if (cp_ij >= 0) continue; Cap f = b[i] > cap[e] ? cap[e] : b[i]; if (b[j] <= 0 and b[j] + f > 0) que.push_back(j); b[i] -= f, b[j] += f, cap[e] -= f, cap[e ^ 1] += f; } if (b[i] > 0) { // Relabel bool flg = false; for (int e : to[i]) { if (!cap[e]) continue; Cost x = p[opposite[e]] - cost[e] - eps; if (!flg or x > p[i]) flg = true, p[i] = x; } que.push_back(i), iters[i] = 0; } } } RetCost ret = 0; for (int e = 0; e < int(cap.size()); e += 2) ret += RetCost(cost[e]) * cap[e ^ 1]; return ret / (_n + 1); } std::vector potential() const { std::vector ret = p, c0 = cost; for (auto &x : ret) x /= (_n + 1); for (auto &x : c0) x /= (_n + 1); while (true) { bool flg = false; for (int i = 0; i < _n; i++) { for (const auto e : to[i]) { if (!cap[e]) continue; int j = opposite[e]; auto y = ret[i] + c0[e]; if (ret[j] > y) ret[j] = y, flg = true; } } if (!flg) break; } return ret; } struct edge { int from, to; Cap cap, flow; Cost cost; }; edge get_edge(int e) const { int m = cap.size() / 2; assert(e >= 0 and e < m); return {opposite[e * 2 + 1], opposite[e * 2], cap[e * 2] + cap[e * 2 + 1], cap[e * 2 + 1], cost[e * 2] / (_n + 1)}; } std::vector edges() const { int m = cap.size() / 2; std::vector result(m); for (int i = 0; i < m; i++) result[i] = get_edge(i); return result; } }; int main() { int N, M, K; cin >> N >> M >> K; vector A(N), B(M); cin >> A >> B; map> mp; REP(i, N) { mp[A.at(i) % K].emplace_back(A.at(i) / K, 0); } REP(j, M) { mp[B.at(j) % K].emplace_back(B.at(j) / K, 1); } lint ret = 0; int nmatch = 0; for (auto [key, vs] : mp) { sort(ALL(vs)); dbg(vs); int n0 = 0, n1 = 0; for (auto [x, t] : vs) { (t ? n1 : n0)++; } nmatch += min(n0, n1); if (n0 > n1) { swap(n0, n1); for (auto &[x, t] : vs) t ^= 1; } mcf_costscaling graph(vs.size() + 1); graph.add_supply(vs.size(), -n0); REP(i, vs.size()) { auto [x, t] = vs.at(i); if (t == 0) { graph.add_supply(i, 1); } else { graph.add_edge(i, vs.size(), 1, 0); } } FOR(i, 1, vs.size()) { int dx = vs.at(i).first - vs.at(i - 1).first; graph.add_edge(i - 1, i, n0, dx); graph.add_edge(i, i - 1, n0, dx); } ret += graph.solve(); assert(n0 <= n1); } if (nmatch < min(N, M)) ret = -1; cout << ret << endl; }