From e473ca606d6169255518109777b5de794dc53020 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Fri, 29 Dec 2017 10:31:03 +0900 Subject: [PATCH 01/80] Create _geom.cc --- geometry/_geom.cc | 3624 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3624 insertions(+) create mode 100644 geometry/_geom.cc diff --git a/geometry/_geom.cc b/geometry/_geom.cc new file mode 100644 index 0000000..9b4a7a4 --- /dev/null +++ b/geometry/_geom.cc @@ -0,0 +1,3624 @@ +// +// +// http://cs.smith.edu/classwiki/images/c/c8/Mitchell.MinPerim.pdf +// https://www.ics.uci.edu/~eppstein/pubs/EppOveRot-DCG-92.pdf +// https://en.wikipedia.org/wiki/Well-separated_pair_decomposition +// http://www.algorithmic-solutions.info/leda_manual/geo_alg.html +// +// future work: +// boolean operations (polygonal region overlay; 人間には実装無理では) +// ham sandwitch cut https://www.ti.inf.ethz.ch/ew/lehre/CG09/materials/v11.pdf + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(x,a) { auto y=(x); if (sign(y-a)) { cout << "line " << __LINE__ << #x << " = " << y << " != " << a << endl; exit(-1); } } +double urand() { return rand() / (1.0 + RAND_MAX); } + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} + +template +ostream &operator<<(ostream &os, const vector &v) { + os << "["; + for (int i = 0; i < v.size(); os << v[i++]) + if (i > 0) os << " "; + os << "]"; + return os; +} +template +ostream &operator<<(ostream &os, const vector> &v) { + os << "["; + for (int i = 0; i < v.size(); os << v[i++]) + if (i > 0) os << endl << " "; + os << "]"; + return os; +} + +const double PI = acos(-1.0); + +// implementation note: use EPS only in this function +// usage note: check sign(x) < 0, sign(x) > 0, or sign(x) == 0 +const double EPS = 1e-8; +int sign(double x) { + if (x < -EPS) return -1; + if (x > +EPS) return +1; + return 0; +} + +using real = long double; +struct point { + real x, y; + point &operator+=(point p) { x += p.x; y += p.y; return *this; } + point &operator-=(point p) { x -= p.x; y -= p.y; return *this; } + point &operator*=(real a) { x *= a; y *= a; return *this; } + point &operator/=(real a) { return *this *= (1.0/a); } + point operator-() const { return {-x, -y}; } + bool operator<(point p) const { + int s = sign(x - p.x); + return s ? s < 0 : sign(y - p.y) < 0; + } +}; +bool operator==(point p, point q) { return !(p < q) && !(q < p); } +bool operator!=(point p, point q) { return p < q || q < p; } +bool operator<=(point p, point q) { return !(q < p); } +point operator+(point p, point q) { return p += q; } +point operator-(point p, point q) { return p -= q; } +point operator*(real a, point p) { return p *= a; } +point operator*(point p, real a) { return p *= a; } +point operator/(point p, real a) { return p /= a; } +real dot(point p, point q) { return p.x*q.x+p.y*q.y; } +real cross(point p, point q) { return p.x*q.y-p.y*q.x; } // left turn > 0 +real norm2(point p) { return dot(p,p); } +point orth(point p) { return {-p.y, p.x}; } +real norm(point p) { return sqrt(dot(p,p)); } +real arg(point p) { return atan2(p.y, p.x); } +real arg(point p, point q){ return atan2(cross(p,q), dot(p,q)); } + +istream &operator>>(istream &is, point &p) { is>>p.x>>p.y;return is; } +ostream &operator<<(ostream &os, const point &p) { os<<"("< 0 && p.y >= 0) return i; + return 0; + } + bool operator()(point p, point q) const { + p = p - o; q = q - o; + if (quad(p) != quad(q)) return s*quad(p) < s*quad(q); + if (cross(p, q)) return s*cross(p, q) > 0; + return norm2(p) < norm2(q); // closer first + } +}; + +struct line { point p, q; }; +bool operator==(line l, line m) { + return !sign(cross(l.p-l.q,m.p-m.q)) && !sign(cross(l.p-l.q,m.p-l.p)); +} + +struct segment { point p, q; }; +bool operator==(segment l, line m) { + return (l.p==m.p && l.q==m.q) || (l.p==m.q && l.q==m.p); // do not consider the direction +} +struct circle { point p; real r; }; +bool operator==(circle c, circle d) { return c.p == d.p && !sign(c.r - d.r); } + +// inner tangent polygon. +// sometimes, circle problem can be reduced to discretized problem +vector discretize(circle c, int n = 50) { + vector ps; + for (int i = 0; i < n; ++i) { + double x = cos(2*PI*i/n), y = sqrt(1 - x*x); + ps.push_back(c.p + c.r * point({x,y})); + } + return ps; +} + +typedef vector polygon; + + +//----------------------------------------------------------------------------- +// visualizer +//----------------------------------------------------------------------------- +struct visualizer { + real minx, maxx, miny, maxy, scale; + ofstream ofs; + visualizer(string s = "data.js") : ofs(s) { + minx = miny = 1.0/0.0; + maxx = maxy = -1.0/0.0; + } + void update(point p) { + minx = min(minx, p.x); miny = min(miny, p.y); + maxx = max(maxx, p.x); maxy = max(maxy, p.y); + scale = 480/(max(maxx-minx, max(maxy-miny,1.0l))); + } + double X(point p) { return scale * (p.x-minx) + 10; } + double Y(point p) { return 490 - scale * (p.y-miny); } + vector ps; + vector ss; + vector cs; + visualizer &operator<<(circle c) { + cs.push_back(c); + update(c.p + point({ c.r, c.r})); + update(c.p + point({-c.r,-c.r})); + return *this; + } + visualizer &operator<<(point p) { + ps.push_back(p); + update(p); + return *this; + } + visualizer &operator<<(segment s) { + ss.push_back(s); + update(s.p); + update(s.q); + return *this; + } + ~visualizer() { + for (point p: ps) + ofs << "circle(" << X(p) << "," << Y(p) << ",3.0)" << endl; + for (segment s: ss) + ofs << "line(" << X(s.p) << "," << Y(s.p) << "," + << X(s.q) << "," << Y(s.q) << ")" << endl; + for (circle c: cs) + ofs << "circle(" << X(c.p) << "," << Y(c.p) << "," << scale*c.r << ")" << endl; + } +}; + + +//----------------------------------------------------------------------------- +// point p is on line l +//----------------------------------------------------------------------------- +vector intersect(line l, point p) { + if (sign(cross(l.p-p, l.q-p)) != 0) return {}; + return {p}; +} +vector intersect(point p, line l) { + return intersect(l, p); +} + +//----------------------------------------------------------------------------- +// point p is on segment s +//----------------------------------------------------------------------------- +vector intersect(segment s, point p) { + if (sign(cross(s.p-p, s.q-p)) != 0) return {}; + if (sign( dot(s.p-p, s.q-p)) > 0) return {}; // > for strict intersect + return {p}; // >= for weak intersect +} +vector intersect(point p, segment s) { + return intersect(s, p); +} + +//----------------------------------------------------------------------------- +// intersection of two lines +// +// Derivation: The crosspoint equation is +// l.p + (l.q - l.p) t == m.p + (m.q - m.p) s ... (CP.1) +// Taking a cross product with (m.q - m.p), we have +// (m.q - m.p) x (l.q - l.p) t == (m.q - m.p) x (m.p - l.p) +// ~~~~~~~~~~~~a~~~~~~~~~~~~ ~~~~~~~~~~~~b~~~~~~~~~~~~ +// +// If a != 0, these two lines have a proper crosspoint. +// If a == 0 and b == 0, these two lines are the same line. +// Otherwise, these two lines are parallel. +//----------------------------------------------------------------------------- +vector intersect(line l, line m) { + auto a = cross(m.q - m.p, l.q - l.p); + auto b = cross(l.p - m.p, l.q - l.p); + if ( sign(a)) return {m.p + b/a*(m.q - m.p)}; // properly crossing + if (!sign(b)) return {m.p, m.q}; // same line + return {}; // disjoint parallel +} + +//----------------------------------------------------------------------------- +// intersection of line and segment +// +// In CP.1, t must be in [0, 1]. Thus 0 <= b/a <= 1 is required. +// By assuming a > 0, it is equivalent to b >= 0 and b-a <= 0 +//----------------------------------------------------------------------------- +vector intersect(line l, segment s) { + auto a = cross(s.q - s.p, l.q - l.p); + auto b = cross(l.p - s.p, l.q - l.p); + if (a < 0) { a *= -1; b *= -1; } + if (sign(b) < 0 || sign(a-b) < 0) return {}; // no intersect + if (sign(a) != 0) return {s.p + b/a*(s.q - s.p)}; // properly crossing + if (sign(b) == 0) return {s.p, s.q}; // same line + return {}; // disjoint parallel +} + +//----------------------------------------------------------------------------- +// intersection of two segments +// +// Solve two CP.1s simultaneously. +// If two segments are overlapping, it is bit difficult. +//----------------------------------------------------------------------------- +vector intersect(segment s, segment t) { + auto a = cross(s.q - s.p, t.q - t.p); + auto b = cross(t.p - s.p, t.q - t.p); + auto c = cross(s.q - s.p, s.p - t.p); + if (a < 0) { a = -a; b = -b; c = -c; } + if (sign(b) < 0 || sign(a-b) < 0 || + sign(c) < 0 || sign(a-c) < 0) return {}; // disjoint + if (sign(a) != 0) return {s.p + b/a*(s.q - s.p)}; // properly crossing + vector ps; // same line + auto insert_if_possible = [&](point p) { + for (auto q: ps) if (sign(dot(p-q, p-q)) == 0) return; + ps.push_back(p); + }; + if (sign(dot(s.p-t.p, s.q-t.p)) <= 0) insert_if_possible(t.p); + if (sign(dot(s.p-t.q, s.q-t.q)) <= 0) insert_if_possible(t.q); + if (sign(dot(t.p-s.p, t.q-s.p)) <= 0) insert_if_possible(s.p); + if (sign(dot(t.p-s.q, t.q-s.q)) <= 0) insert_if_possible(s.q); + return ps; +} + +//----------------------------------------------------------------------------- +// reflected point with respect to l +//----------------------------------------------------------------------------- +point reflection(point p, line l) { + auto a = dot(l.p-l.q, l.p-l.q); + auto b = dot(l.p-p, l.p-l.q); + return 2 * (l.p + a/b*(l.q - l.p)) - p; +} + +//----------------------------------------------------------------------------- +// closest point on l +//----------------------------------------------------------------------------- +point projection(point p, line l) { + auto a = dot(l.p-l.q, l.p-l.q); + auto b = dot(l.p-p, l.p-l.q); + return l.p + a/b*(l.q - l.p); +} + +//----------------------------------------------------------------------------- +// closest point on s +//----------------------------------------------------------------------------- +point projection(point p, segment s) { + auto a = dot(s.p-s.q, s.p-s.q); + auto b = dot(s.p - p, s.p-s.q); + if (sign(b) < 0) return s.p; + if (sign(a-b) < 0) return s.q; + return s.p + b/a*(s.q - s.p); +} + +//----------------------------------------------------------------------------- +// closest point on c +//----------------------------------------------------------------------------- +point projection(point p, circle c) { + point v = p - c.p; + return c.p + c.r * v / norm(v); +} +//----------------------------------------------------------------------------- +// distances +//----------------------------------------------------------------------------- +real dist(point p, point q) { + return norm(p-q); +} +real dist(point p, line l) { + return dist(p, projection(p, l)); +} +real dist(line l, point p) { + return dist(p, l); +} +real dist(line l, line m) { + if (sign(cross(l.p-l.q,m.p-m.q))) return 0; // cross + return dist(l.p, m); +} +real dist(point p, segment s) { + return dist(p, projection(p, s)); +} +real dist(line l, segment s) { + if (intersect(l, s).size()) return 0; + return min(dist(l, s.p), dist(l, s.q)); +} +real dist(segment s, line l) { + return dist(l, s); +} +real dist(segment s, segment T) { + if (intersect(s, T).size()) return 0; + return min({dist(s.p,T), dist(s.q,T), dist(T.p,s), dist(T.q,s)}); +} + + +//----------------------------------------------------------------------------- +// intersection points of two circles +//----------------------------------------------------------------------------- +vector intersect(circle c, circle d) { + if (c.r < d.r) swap(c, d); + auto pow2 = [](real a) { return a*a; }; + auto g = dot(c.p-d.p, c.p-d.p); + if (sign(g) == 0) { + if (sign(c.r-d.r) != 0) return {}; + return {{c.p.x+c.r, c.p.y}, {c.p.x, c.p.y+c.r}, {c.p.x-c.r, c.p.y}}; + } + int inner = sign(g-pow2(c.r-d.r)); + int outer = sign(g-pow2(c.r+d.r)); + if (inner < 0 || outer > 0) return {}; + if (inner == 0) return {(c.r*d.p-d.r*c.p)/(c.r-d.r)}; + if (outer == 0) return {(c.r*d.p+d.r*c.p)/(c.r+d.r)}; + auto eta = (pow2(c.r) - pow2(d.r) + g)/(2*g); + auto mu = sqrt(pow2(c.r)/g - pow2(eta)); + point q = c.p + eta*(d.p-c.p), v = mu*orth(d.p - c.p); + return {q + v, q - v}; +} + +//----------------------------------------------------------------------------- +// intersection of line and circle +//----------------------------------------------------------------------------- +vector intersect(line l, circle c) { + point u = l.q - l.p, v = l.p - c.p; + auto a = dot(u,u), b = dot(u,v)/a, t = (dot(v,v) - c.r*c.r)/a; + auto det = b*b - t; + if (sign(det) < 0) return {}; // no solution + if (sign(det) == 0) return {l.p - b*u}; // touch inner/outer + return {l.p - (b + sqrt(det))*u, // properly intersect + l.p - (b - sqrt(det))*u}; +} +vector intersect(circle c, line l) { + return intersect(l, c); +} + +//----------------------------------------------------------------------------- +// intersection of segment and circle +// number of points: +// 0 ==> no intersect +// 1 ==> touch +// 2 ==> contained +// 3 ==> penetrating single side +// 4 ==> penetrating both sides +// sorted from s.p to s.q; usually, one would use ans[0] and ans.back(). +//----------------------------------------------------------------------------- +vector intersect(circle c, segment s) { + point u = s.q - s.p, v = s.p - c.p; + auto a = dot(u,u), b = dot(u,v)/a, t = (dot(v,v) - c.r*c.r)/a; + auto det = b*b - t; + if (sign(det) < 0) return {}; // no solution + + auto t1 = -b - sqrt(det), t2 = -b + sqrt(det); + vector ps; + auto insert_if_possible = [&](point p) { + for (auto q: ps) if (sign(dot(p-q, p-q)) == 0) return; + ps.push_back(p); + }; + if (sign(c.r - norm(s.p-c.p)) >= 0) insert_if_possible(s.p); + if (sign(t1) >= 0 && sign(1-t1) >= 0) insert_if_possible(s.p+t1*u); + if (sign(t2) >= 0 && sign(1-t2) >= 0) insert_if_possible(s.p+t2*u); + if (sign(c.r - norm(s.q-c.p)) >= 0) insert_if_possible(s.q); + return ps; +} +vector intersect(segment s, circle c) { + return intersect(c, s); +} +bool contains(circle c, point p) { + return sign(dot(c.p-p, c.p-p) - c.r * c.r) <= 0; +} + + +//----------------------------------------------------------------------------- +// polygon contains a point +// half-line crossing method +//----------------------------------------------------------------------------- +int contains(polygon ps, point p) { + bool in = false; + for (int i = 0; i < ps.size(); ++i) { + int j = (i+1 == ps.size() ? 0 : i+1); + point a = ps[i] - p, b = ps[j] - p; + if (a.y > b.y) swap(a, b); + if (a.y <= 0 && 0 < b.y && cross(a, b) < 0) in = !in; + if (!sign(cross(a, b)) && sign(dot(a, b)) <= 0) + return 1; // point on the edge + } + return in ? 2 : 0; // point in:out the polygon +} + +//----------------------------------------------------------------------------- +// convex hull with the given points +// +// Andrew's monotone chain +// +// [verified] +// +// A. M. Andrew (1979): +// Another efficient algorithm for convex hulls in two dimensions. +// Information Processing Letters 9 (5): 216-219. +//----------------------------------------------------------------------------- +polygon convex_hull(vector ps) { + int n = ps.size(), k = 0; + sort(all(ps)); + vector ch(2*n); + auto cond = [&](point p, point q, point o) { + int a = sign(cross(p-o, q-o)); // return a < 0 if no three points on line + return a ? a < 0 : sign(dot(p-o, q-o)) >= 0; + }; + for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull + for (; k >= 2 && cond(ch[k-2], ch[k-1], ps[i]); --k); + for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull + for (; k >= t && cond(ch[k-2], ch[k-1], ps[i]); --k); + ch.resize(k-1); + return ch; +} + + +//----------------------------------------------------------------------------- +// farthest pair of points +// +// is located on the convex hull. perform rotating caliper. +// +// [verified ACAC002] +//----------------------------------------------------------------------------- +pair farthest_pair(vector ps) { + vector ch = convex_hull(ps); + int n = ch.size(); + int u = 0, v = 1; + real best = -1; + for (int i = 0, j = 1; i < n; ++i) { + while (1) { + int k = (j+1 < n ? j+1 : 0); + real len = norm2(ch[j] - ch[i]); + if (sign(len - norm2(ch[k] - ch[i])) <= 0) j = k; + else { + if (best < len) { best = len; u = i; v = j; } + break; + } + } + } + return make_pair(ch[u], ch[v]); +} + + +//----------------------------------------------------------------------------- +// signed area of arbitrary polygon +// +// [verified] +//----------------------------------------------------------------------------- +real area(polygon ps) { + if (ps.size() <= 2) return 0; + auto a = cross(ps.back(), ps[0]); + for (int i = 0; i+1 < ps.size(); ++i) + a += cross(ps[i], ps[i+1]); + return a/2; +} + +//----------------------------------------------------------------------------- +// left side of a convex polygon with respect to a line +// +// [verified] +//----------------------------------------------------------------------------- +polygon convex_cut(polygon ps, line l) { + vector qs; + for (int i = 0; i < ps.size(); ++i) { + int j = (i+1 == ps.size() ? 0 : i+1); + if (sign(cross(l.p - ps[i], l.q - ps[i])) >= 0) qs.push_back(ps[i]); + if (sign(cross(l.p - ps[i], l.q - ps[i])) * + sign(cross(l.p - ps[j], l.q - ps[j])) < 0) { + auto a = cross(ps[j] - ps[i], l.q - l.p); + auto b = cross(l.p - ps[i], l.q - l.p); + qs.push_back(ps[i] + b/a*(ps[j] - ps[i])); + } + } + return qs; +} + +//----------------------------------------------------------------------------- +// the circle through three points +//----------------------------------------------------------------------------- +circle three_point_circle(point p, point q, point r) { + point u = orth(q - p), v = r - p; + point o = (p+q + u*dot(r-q,v)/dot(u,v))/2; + return {o, norm(p-o)}; +} +//----------------------------------------------------------------------------- +// area of quadrilateral; +// not verified +//----------------------------------------------------------------------------- +real quadrilateral_area(real a, real b, real c, real d) { + real s = a+b+c+d; + return sqrt((s-a)*(s-b)*(s-c)*(s-d))/4; +} +real triangle_area(real a, real b, real c) { + return quadrilateral_area(a, b, c, 0); +} +real excircle_radius(real a, real b, real c) { + return a*b*c/4/triangle_area(a, b, c); +} +real incircle_radius(real a, real b, real c) { + return 2*triangle_area(a,b,c)/(a+b+c); +} + +//----------------------------------------------------------------------------- +// tangent line of a circle at the nearest point from p +// +// Let q be a tangent point. +// The angle between q -- p -- c.p is +// sin(t) = r/|p - c.p|. +// and the solution is +// p + (c.p - p) * exp(\pm it). +// +// [verified] +//----------------------------------------------------------------------------- +vector tangent(point p, circle c) { + point u = p - c.p, v = orth(u); + auto g = dot(u,u) - c.r*c.r; + if (sign(g) < 0) return {}; + if (sign(g) == 0) return {{p, p + v}}; + u = u * (c.r*c.r/dot(u,u)); + v = v * (c.r*sqrt(g)/dot(v,v)); + return {{p, c.p + u - v}, {p, c.p + u + v}}; +} +vector tangent(circle c, point p) { + return tangent(p, c); +} + +//----------------------------------------------------------------------------- +// common tangents of two circles +// +// [verified] +//----------------------------------------------------------------------------- +vector tangent(circle c, circle d) { + if (c.r < d.r) swap(c, d); + auto g = dot(c.p-d.p, c.p-d.p); + if (sign(g) == 0) return {}; // same origin + point u = (d.p-c.p)/sqrt(g), v = orth(u); // coordinate systems + vector ls; + for (int s = +1; s >= -1; s -= 2) { + auto h = (c.r+s*d.r)/sqrt(g); // = cos(theta) + if (sign(1 - h*h) == 0) { // touch inner/outer + ls.push_back({c.p+c.r*u, c.p+c.r*(u+v)}); + } else if (sign(1 - h*h) > 0) { // properly intersect + point uu = h*u, vv = sqrt(1-h*h)*v; + ls.push_back({c.p+c.r*(uu+vv), d.p-d.r*(uu+vv)*s}); + ls.push_back({c.p+c.r*(uu-vv), d.p-d.r*(uu-vv)*s}); + } + } + return ls; +} + +//----------------------------------------------------------------------------- +// common tangent circles of two lines of radius r +// +// o|o +// --+-- +// o|o +// +// number of solutions: +// 0: parallel and distance < r +// 1: parallel and distance = r +// 4: non-parallel +// +// [non-verified] +//----------------------------------------------------------------------------- +vector tangent_circles(line l, line m, real r) { + vector cs; + real a = cross(l.p-m.p, l.q-l.p), b = cross(m.q-m.p, l.q-l.p); + if (!sign(b)) { // parallel case + if (l.q < l.p) swap(l.p, l.q); + if (m.q < m.p) swap(m.p, m.q); + if (sign(cross(m.p-l.p, m.q-l.p)) >= 0) swap(l, m); // l is lower + point v = orth(m.q - m.p); v /= norm(v); + real d = a / cross(l.q - l.p, v); + if (sign(d - 2*r) == 0) cs.push_back({l.p + r * v, r}); + } else { + point o = m.p + a/b * (m.q - m.p), u = l.q - l.p, v = m.q - m.p; + u /= norm(u); v /= norm(v); + for (int i = 0; i < 4; ++i) { + point w = u + v; w /= norm(w); + real t = r / sqrt(1 - dot(v,w)*dot(v,w)); + cs.push_back({o + t * w, r}); + u *= -1; swap(u, v); + } + } + return cs; +} + +//----------------------------------------------------------------------------- +// find a line that passes the maximum number of points +// +// [non-verified] +//----------------------------------------------------------------------------- +int maximum_points_line(vector ps) { + sort(all(ps)); // assume dx >= 0; if dx == 0 then dy >= 0 + int max_count = 0; + for (int i = 0; i < ps.size(); ++i) { + vector qs; + int base = 1; + for (int j = 0; j < i; ++j) + if (ps[j] == ps[i]) ++base; + else qs.push_back(ps[j] - ps[i]); + sort(all(qs), polar_angle()); + for (int j = 0, k; j < qs.size(); j += k) { + for (k = 1; j+k < qs.size() && sign(cross(qs[j], qs[j+k])) == 0; ++k); + max_count = max(max_count, base + k); + } + } + return max_count; +} + +// for comparison +int maximum_points_line_n(vector ps) { + sort(all(ps)); + int ans = 0; + for (int i = 0; i < ps.size(); ++i) { + for (int j = i+1; j < ps.size(); ++j) { + if (ps[i] == ps[j]) continue; + int count = 0; + for (int k = 0; k < ps.size(); ++k) { + if (intersect((line){ps[i], ps[j]}, ps[k]).size() > 0) ++count; + } + ans = max(ans, count); + } + } + return ans; +} +void verify_maximum_points_line() { + int n = 100; + vector ps(n); + for (int i = 0; i < n; ++i) { + ps[i].x = rand() % 20; + ps[i].y = rand() % 20; + } + cout << maximum_points_line(ps) << endl; + cout << maximum_points_line_n(ps) << endl; +} + + +//----------------------------------------------------------------------------- +// triangulate simple polygon in O(n) time. +// +// [non-verified]; future work for polygonal overlay +//----------------------------------------------------------------------------- +real triangulate(vector ps) { + int n = ps.size(); + vector next(n); + for (int i = 0; i < n-1; ++i) next[i] = i+1; + auto is_ear = [&](int i, int j, int k) { + if (sign(cross(ps[j]-ps[i], ps[k]-ps[i])) <= 0) return false; + for (int l = next[k]; l != i; l = next[l]) + if (sign(cross(ps[i]-ps[l], ps[j]-ps[l])) >= 0 + && sign(cross(ps[j]-ps[l], ps[k]-ps[l])) >= 0 + && sign(cross(ps[k]-ps[l], ps[i]-ps[l])) >= 0) return false; + return true; + }; + real area = 0; + for (int i = 0; next[next[i]] != i; ) { + if (is_ear(i, next[i], next[next[i]])) { + area += abs(cross(ps[next[i]]-ps[i], ps[next[next[i]]] - ps[i])) / 2; + next[i] = next[next[i]]; + } else i = next[i]; + } + return area; +} + +//----------------------------------------------------------------------------- +// area of intersection of two circles +// [verified] +//----------------------------------------------------------------------------- +real intersection_area(circle c, circle d) { + if (c.r < d.r) swap(c, d); + auto A = [&](real r, real h) { + return r*r*acos(h/r)-h*sqrt(r*r-h*h); + }; + auto l = norm(c.p - d.p), a = (l*l + c.r*c.r - d.r*d.r)/(2*l); + if (sign(l - c.r - d.r) >= 0) return 0; // far away + if (sign(l - c.r + d.r) <= 0) return PI*d.r*d.r; + if (sign(l - c.r) >= 0) return A(c.r, a) + A(d.r, l-a); + else return A(c.r, a) + PI*d.r*d.r - A(d.r, a-l); +} +//----------------------------------------------------------------------------- +// circle-polygon intersection area +// [verified] +//----------------------------------------------------------------------------- +real intersection_area(vector ps, circle c) { + auto tri = [&](point p, point q){ + point d = q - p; + auto a = dot(d,p)/dot(d,d), b = (dot(p,p)-c.r*c.r)/dot(d,d); + auto det = a*a - b; + if (det <= 0) return arg(p,q) * c.r*c.r / 2; + auto s = max(0.l, -a-sqrt(det)), t = min(1.l, -a+sqrt(det)); + if (t < 0 || 1 <= s) return c.r*c.r*arg(p,q)/2; + point u = p + s*d, v = p + t*d; + return arg(p,u)*c.r*c.r/2 + cross(u,v)/2 + arg(v,q)*c.r*c.r/2; + }; + auto sum = 0.0; + for (int i = 0; i < ps.size(); ++i) + sum += tri(ps[i] - c.p, ps[(i+1)%ps.size()] - c.p); + return sum; +} +real intersection_area(circle c, vector ps) { + return intersection_area(ps, c); +} + +//----------------------------------------------------------------------------- +// find the closest pair of points by sweepline +// [verified] +//----------------------------------------------------------------------------- +pair closest_pair(vector ps) { + sort(all(ps), [](point p, point q) { return p.y < q.y; }); + auto u = ps[0], v = ps[1]; + auto best = dot(u-v, u-v); + auto update = [&](point p, point q) { + auto dist = dot(p-q, p-q); + if (best > dist) { best = dist; u = p; v = q; } + }; + set S; S.insert(u); S.insert(v); + for (int l = 0, r = 2; r < ps.size(); ++r) { + if (S.count(ps[r])) return {ps[r], ps[r]}; + if ((ps[l].y-ps[r].y)*(ps[l].y-ps[r].y) > best) S.erase(ps[l++]); + auto i = S.insert(ps[r]).fst; + for (auto j = i; ; ++j) { + if (j == S.end() || (i->x-j->x)*(i->x-j->x) > best) break; + if (i != j) update(*i, *j); + } + for (auto j = i; ; --j) { + if (i != j) update(*i, *j); + if (j == S.begin() || (i->x-j->x)*(i->x-j->x) > best) break; + } + } + return {u, v}; +} +//----------------------------------------------------------------------------- +// find the closest pair of points by divide and conquer +// (slower than the sweepline) +//----------------------------------------------------------------------------- +pair closest_pair2(vector ps) { + sort(all(ps), [](point p, point q) { return p.y < q.y; }); + auto u = ps[0], v = ps[1]; + auto best = dot(u-v, u-v); + auto update = [&](point p, point q) { + auto dist = dot(p-q, p-q); + if (best > dist) { best = dist; u = p; v = q; } + }; + function rec = [&](int l, int r) { + if (r - l <= 1) { + for (int i = l; i < r; ++i) + for (int j = i+1; j < r; ++j) + update(ps[i], ps[j]); + stable_sort(&ps[l], &ps[r]); + } else { + int m = (l + r) / 2; + auto y = ps[m].y; + rec(l, m); rec(m, r); + inplace_merge(&ps[l], &ps[m], &ps[r]); + vector qs; + for (int i = l; i < r; ++i) { + if ((ps[i].y-y)*(ps[i].y-y) >= best) continue; + for (int j = (int)qs.size()-1; j >= 0; --j) { + if ((qs[j].x-ps[i].x)*(qs[j].x-ps[i].x) >= best) break; + update(qs[j], ps[i]); + } + qs.push_back(ps[i]); + } + } + }; + rec(0, ps.size()); + return {u, v}; +} + + +//----------------------------------------------------------------------------- +// half-plane intersection in O(n log n) +// [un-verified; buggy?] +//----------------------------------------------------------------------------- +vector half_plane_intersection(vector ls) { + int n = ls.size(); + sort(all(ls), [&](line l, line m) { return arg(l.p-l.q) < arg(m.p-m.q); }); + int L = 0, R = 0; + vector bd(2*n); + vector ps(2*n); + bd[R] = ls[0]; + auto left = [&](point p, line l) { return sign(cross(l.p-p, l.q-p)) >= 0; }; + for (int i = 1; i < n; ++i) { + if (!sign(cross(bd[R].p-bd[R].q, ls[i].p-ls[i].q))) { + if (left(ls[i].p, bd[R])) bd[R] = ls[i]; + } else { + while (L < R && !left(ps[R-1], ls[i])) --R; + while (L < R && !left(ps[L] , ls[i])) ++L; + bd[++R] = ls[i]; + } + if (R > L) ps[R-1] = intersect(bd[R-1], bd[R])[0]; + } + while (L < R && !left(ps[R-1], bd[L])) --R; + if (R - L <= 1) return {}; + if (R > L) ps[R] = intersect(bd[L], bd[R])[0]; + vector ch = {ps[L]}; + for (int i = L+1; i <= R; ++i) + if (!(ch.back() == ps[i])) ch.push_back(ps[i]); + if (ch.size() > 1 && ch.back() == ch[0]) ch.pop_back(); + return ch; +} + + + +//----------------------------------------------------------------------------- +// geometric median, i.e., p in argmin sum |ps[i] - p|_2 +// +// Weiszfield's iterative reweighted method least squares +// with avoiding equal points +// [verified: LA5102] +//----------------------------------------------------------------------------- +point geometric_median(vector ps) { + point g = {0,0}; + real eta = 1000.0; + for (int iter = 0; iter < 1000000; ++iter, eta /= 2) { + real w = 0; + point h = {0,0}; + for (point p: ps) { + real a = eta + norm(p - g); + h = h + p/a; w = w + 1/a; + } + h = h / w; swap(g, h); + if (norm(g - h) < EPS) break; + } + return g; +} + + +//----------------------------------------------------------------------------- +// find a circle of radius r that contains many points as possible +// +// quad-tree search (this is faster than the next sweepline solution) +// [verified: AOJ 1132] +//----------------------------------------------------------------------------- +int maximum_circle_cover(vector ps, double r) { + const double dx[] = {1,-1,-1,1}, dy[] = {1,1,-1,-1}; + point best_p; + int best = 0; + function)> + rec = [&](point p, double w, vector ps) { + w /= 2; + point qs[4]; + vector pss[4]; + for (int i = 0; i < 4; ++i) { + qs[i] = p + w * point({dx[i], dy[i]}); + int lo = 0; + for (point q: ps) { + auto d = dist(qs[i], q); + if (sign(d - r) <= 0) ++lo; + if (sign(d - w*sqrt(2) - r) <= 0) pss[i].push_back(q); + } + if (lo > best) { best = lo; best_p = qs[i]; } + } + for (int i = 0, j; i < 4; ++i) { + for (int j = i+1; j < 4; ++j) + if (pss[i].size() < pss[j].size()) + swap(pss[i], pss[j]), swap(qs[i], qs[j]); + if (pss[i].size() <= best) break; + rec(qs[i], w, pss[i]); + } + }; + real w = 0; + for (point p: ps) w = max(w, max(abs(p.x), abs(p.y))); + rec({0,0}, w, ps); + return best; //best_p; +} + +//----------------------------------------------------------------------------- +// find a circle of radius r that contains many points as possible +// +// sweepline O(n^2 log n). +// [verified: AOJ 1132] +//----------------------------------------------------------------------------- +int maximum_circle_cover2(vector ps, double r) { + int best = 0; + for (point p: ps) { + int count = 0; + vector> aux; + for (point q: ps) { + auto d = dist(p, q); + if (sign(d) == 0) ++count; + else if (sign(d - 2*r) <= 0) { + auto theta = arg(q-p); + auto phi = acos(min(1.l, d/(2*r))); + aux.push_back({theta-phi, -1}); + aux.push_back({theta+phi, +1}); + } + } + sort(all(aux)); + best = max(best, count); + for (auto a: aux) + best = max(best, count -= a.snd); + } + return best; +} + +//----------------------------------------------------------------------------- +// area of union of rectangles +// Bentley's sweepline with segment tree. +// +// [accepted, LightOJ 1120 Rectangle Union] +//----------------------------------------------------------------------------- +struct rectangle { point p, q; }; // lower-left and upper-right +real rectangle_union(vector rs) { + vector ys; // plane sweep with coordinate compression + struct event { + real x, ymin, ymax; + int add; + bool operator<(event e) const { return x < e.x; } + }; + vector es; + for (auto r: rs) { + ys.push_back(r.p.y); + ys.push_back(r.q.y); + es.push_back({r.p.x, r.p.y, r.q.y, +1}); + es.push_back({r.q.x, r.p.y, r.q.y, -1}); + } + sort(all(es)); + sort(all(ys)); + ys.erase(unique(all(ys)), ys.end()); + + vector len(4 * ys.size()); // segment tree on sweepline + vector sum(4 * ys.size()); + function update + = [&](real ymin, real ymax, int add, int i, int j, int k) { + ymin = max(ymin, ys[i]); ymax = min(ymax, ys[j]); + if (ymin >= ymax) return; + if (ys[i] == ymin && ys[j] == ymax) sum[k] += add; + else { + update(ymin, ymax, add, i, (i+j)/2, 2*k+1); + update(ymin, ymax, add, (i+j)/2, j, 2*k+2); + } + if (sum[k]) len[k] = ys[j] - ys[i]; + else len[k] = len[2*k+1] + len[2*k+2]; + }; + real area = 0; + for (int i = 0; i+1 < es.size(); ++i) { + update(es[i].ymin, es[i].ymax, es[i].add, 0, ys.size()-1, 0); + area += len[0] * (es[i+1].x - es[i].x); + } + return area; +} + +//----------------------------------------------------------------------------- +// number of points below ps[i]--ps[j] +// +// TODO: めっちゃバグる・バグってるなう +// +// [non-verified] +//----------------------------------------------------------------------------- +struct points_counter { + int n; + vector ps; + vector> low; // low[i][j] is the number of points below (ps[i], ps[j]) + points_counter(vector ps_) : n(ps_.size()), ps(ps_), low(n, vector(n)) { + double sint = 1e-1, cost = sqrt(1 - sint*sint); // perturbation + for (point &p: ps) p = {p.x*cost - p.y*sint, p.x*sint + p.y*cost}; + + vector is(n); iota(all(is), 0); + sort(all(is), [&](int i, int j) { return ps[i] < ps[j]; }); + + vector left; + vector> iss(n); // iss[i] = points smaller than i, sorted by the polar angle + for (int o: is) { + sort(all(left), [&](int j, int k) { // sort in clock-wise order + point u = ps[j] - ps[o], v = ps[k] - ps[o]; + int s = sign(cross(u, v)); + return s ? s < 0 : dot(u,u) > dot(v,v); + }); + iss[o] = left; + left.push_back(o); + } + for (int o: is) { + cout << "processing " << ps[o] << endl; + cout << iss[o].size() << endl; + for (int i = 0; i+1 < iss[o].size(); ++i) { + int a = iss[o][i], b = iss[o][i+1]; + cout << " comparing " << ps[a] << " " << ps[b] << endl; + if (ps[b] < ps[a]) low[b][o] = low[a][o] + low[a][b] + 1; + else low[b][o] = low[a][o] - low[a][b]; + low[o][b] = low[b][o]; + } + } + cout << low << endl; + + + vector> low2(n, vector(n)); + for (int i = 0; i < n; ++i) { + for (int j = i+1; j < n; ++j) { + for (int k = 0; k < n; ++k) { + if (k == i || k == j) continue; + point p = ps[i], q = ps[j], r = ps[k]; + if (q < p) swap(p, q); // assume p < q + if (r.x < p.x || r.x >= q.x) continue; + if (sign(cross(q-r, p-r)) > 0) low2[i][j]++; + } + low2[j][i] = low2[i][j]; + } + } + cout << low2 << endl; + } + int count(int a, int b, int o) { // i -> j -> k ccw order + if (ps[b] < ps[a]) swap(a, b); + if (ps[o] < ps[b]) swap(b, o); + if (ps[b] < ps[a]) swap(a, b); + if (ps[b].y < ps[a].y) swap(a, b); + cout << "comparing " << ps[a] << " " << ps[b] << " " << ps[o] << endl; + cout << "values " << low[o][a] << " " << low[a][b] << " " << low[o][b] << endl; + return low[o][a] + low[a][b] - low[o][b] + (ps[a].x > ps[b].x); + } +}; + + +//----------------------------------------------------------------------------- +// segment arrangement +// +// graph whose vertices are the points and edges are the segments. +// complexity is O(m^2 log m). +// +// [accepted: AOJ 1226] +// [accepted: AOJ 2068] +// [accepted: AOJ 0273] +//----------------------------------------------------------------------------- +namespace arrangement_slow { +struct arrangement { + struct edge { + int src, dst; + real weight; + size_t id, rev; + }; + int n; + vector ps; + map id; + vector> adj; + + arrangement(vector ss) : n(0) { + vector>> group(ss.size()); + auto append = [&](int i, point p) { + if (!id.count(p)) { id[p] = n++; ps.push_back(p); } + group[i].push_back({norm(ss[i].p - p), id[p]}); + }; + for (int i = 0; i < ss.size(); ++i) { + append(i, ss[i].p); append(i, ss[i].q); + for (int j = 0; j < i; ++j) { + for (point p: intersect(ss[i], ss[j])) { + append(i, p); append(j, p); + } + } + } + adj.resize(n); + for (auto &vs: group) { + sort(all(vs)); + for (int i = 0; i+1 < vs.size(); ++i) { + auto u = vs[i].snd, v = vs[i+1].snd; + if (u == v) continue; + auto len = vs[i+1].fst - vs[i].fst; + adj[u].push_back({u, v, len}); + adj[v].push_back({v, u, len}); + } + } + // remove duplicates and orient edges + vector> idx(n); + for (int u = 0; u < n; ++u) { + auto eq = [&](edge e, edge f) { return e.dst == f.dst; }; + auto lt = [&](edge e, edge f) { return e.dst < f.dst; }; + sort(all(adj[u]), lt); + adj[u].erase(unique(all(adj[u]), eq), adj[u].end()); + sort(all(adj[u]), [&](edge e, edge f) { + return arg(ps[e.dst] - ps[e.src]) > arg(ps[f.dst] - ps[f.src]); + }); + for (int i = 0; i < adj[u].size(); ++i) { + adj[u][i].id = i; + int v = adj[u][i].dst; + idx[u][v] = i; + if (idx[v].count(u)) { + int j = idx[v][u]; + adj[u][i].rev = j; + adj[v][j].rev = i; + } + } + } + } + // traverse utilities for DCEL (planar graph) + // traversing edges surrounding the left face + edge twin(edge e) const { return adj[e.dst][e.rev]; } + edge next(edge e) const { + int j = adj[e.dst][e.rev].id + 1; + if (j >= adj[e.dst].size()) j = 0; + return adj[e.dst][j]; + } + edge outer() const { + int s = 0; // leftmost, outerface edge + for (int i = 1; i < n; ++i) if (ps[i] < ps[s]) s = i; + for (int i = 0; i < adj[s].size(); ++i) { + edge e1 = adj[s][i], e2 = adj[s][(i+1)%adj[s].size()]; + if (cross(ps[e1.dst]-ps[s], ps[e2.dst]-ps[s]) <= 0) return e1; + } + } + + // [accepted: AOJ 2068 Hide-and-Seek] + void shortest_path(point sp) { + int s = id[sp]; + vector dist(n, 1.0/0.0); + vector prev(n, -1); + typedef pair node; + priority_queue, greater> Q; + Q.push(node(dist[s] = 0, s)); + while (!Q.empty()) { + node z = Q.top(); Q.pop(); + if (dist[z.snd] <= z.fst) { + for (auto e: adj[z.snd]) { + if (dist[e.dst] > dist[e.src] + e.weight) { + dist[e.dst] = dist[e.src] + e.weight; + prev[e.dst] = e.src; + Q.push({dist[e.dst], e.dst}); + } + } + } + } + real ans = 0; + for (int u = 0; u < n; ++u) { + for (edge e: adj[u]) { + real s = (dist[e.dst] - dist[e.src] + e.weight)/2; + ans = max(ans, dist[e.src] + s); + } + } + printf("%.12lf\n", ans); + } + + // [accepted: AOJ 1226 Fishnet] + template + void traverse(edge e, F func) { + edge s = e; + do { + func(e); + e = next(e); + } while (e.src != s.src || e.dst != s.dst); + } + void traverse_all_faces() { + vector> visited(n); + real ans = 0; + for (int u = 0; u < n; ++u) { + for (edge e: adj[u]) { + if (!visited[e.src].count(e.dst)) { + real area = 0; + traverse(e, [&](edge e) { + visited[e.src].insert(e.dst); + area += cross(ps[e.src], ps[e.dst]); + }); + ans = max(ans, area); + } + } + } + printf("%.6f\n", ans/2); + } + void traverse_all_faces2() { + vector> visited(n); + real ans = 0; + for (int u = 0; u < n; ++u) { + for (edge e: adj[u]) { + if (!visited[e.src].count(e.dst)) { + real area = 0; + traverse(e, [&](edge e) { + visited[e.src].insert(e.dst); + area += cross(ps[e.src], ps[e.dst]); + }); + if (area > 0) ans += area; + } + } + } + printf("%.12f\n", ans/2); + } + edge contains() { + vector> visited(n); + auto contains = [&](edge e, point p) { + edge s = e; + bool in = false; + do { + point a = ps[e.src] - p, b = ps[e.dst] - p; + if (a.y > b.y) swap(a, b); + if (a.y <= 0 && 0 < b.y && cross(a, b) < 0) in = !in; + if (!sign(cross(a, b)) && sign(dot(a, b)) <= 0) + return 1; // point on the edge + visited[e.src].count(e.dst); + e = next(e); + } while (e.src != s.src || e.dst != s.dst); + return in ? 2 : 0; // point in:out the polygon + }; + for (int u = 0; u < n; ++u) + for (edge e: adj[u]) + if (!visited[e.src].count(e.dst)) + if (contains(e, (point){0,0})) return e; + return {-1}; + } +}; + +void AOJ0273() { + for (int c, w; ~scanf("%d %d", &c, &w) && c; ) { + vector ps(c); + for (int i = 0; i < c; ++i) { + scanf("%lf %lf", &ps[i].x, &ps[i].y); + } + vector ss; + for (int i = 0; i < w; ++i) { + int u, v; scanf("%d %d", &u, &v); + ss.push_back({ps[u-1], ps[v-1]}); + } + arrangement arr(ss); + typedef arrangement::edge edge; + edge e = arr.outer(); // outer-face edge + + vector> step(arr.n); + queue que; + auto proceed = [&](edge s, int value) { + if (step[s.src].count(s.dst)) return; + edge e = s; + do { + step[e.src][e.dst] = value; + que.push(arr.twin(e)); + e = arr.next(e); + } while (e.src != s.src || e.dst != s.dst); + }; + proceed(e, 0); + int ans = 0; + while (!que.empty()) { + edge e = que.front(); que.pop(); + ans = step[e.dst][e.src]; + proceed(e, ans + 1); + } + printf("%d\n", ans); + } +} + + + +void AOJ1226() { + for (int n; ~scanf("%d", &n) && n; ) { + vector> a(4, vector(n)); + for (int k = 0; k < 4; ++k) + for (int i = 0; i < n; ++i) + scanf("%lf", &a[k][i]); + + vector ss = { + {{0,0},{0,1}}, + {{0,1},{1,1}}, + {{1,1},{1,0}}, + {{1,0},{0,0}} + }; + for (int i = 0; i < n; ++i) { + ss.push_back({{a[0][i],0},{a[1][i],1}}); + ss.push_back({{0,a[2][i]},{1,a[3][i]}}); + } + arrangement arr(ss); + arr.traverse_all_faces(); + } +} + +void AOJ2448() { + int n; scanf("%d", &n); + vector ps(n); + for (int i = 0; i < n; ++i) + scanf("%lf %lf", &ps[i].x, &ps[i].y); + vector ss; + for (int i = 0; i+1 < n; ++i) + ss.push_back({ps[i], ps[i+1]}); + arrangement arr(ss); + arr.traverse_all_faces2(); +} + +void AOJ1247() { + for (int n; ~scanf("%d", &n) && n; ) { + vector ss; + for (int i = 0; i < n; ++i) { + double x1, y1, x2, y2; + scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2); + ss.push_back({{x1,y1},{x2,y2}}); + } + arrangement arr(ss); + if (arr.contains().src >= 0) printf("yes\n"); + else printf("no\n"); + } +} +} +// --------------------------------------------------------------------------- +// segment arrangement (Bentley-Ottman's plane sweep) +// +// graph whose vertices are the points and edges are the segments. +// complexity is O((n + k) log n); where k is the number of intersections. +// +// Verified: AOJ1226, AOJ2448 +// --------------------------------------------------------------------------- +struct arrangement { + struct Vertex; struct Edge; // Doubly Connected Edge List + struct Vertex { + point p; // + Edge *edge; // any edge incident to this vertex + }; + struct Edge { + Vertex *vertex; // origin vertex of the edge + Edge *twin; // reverse edge of e + Edge *prev, *next; // surrounding edges of face (CCW) + }; + map> adj; + vector vertices; + vector edges; + + vector segs; + + struct node { // Sweep-Line Structure (RBST) + int index; + int size = 1; + node *left = 0, *right = 0; + } *root = 0; + vector ns; + + node *update(node *x) { + if (x) { + x->size = 1; + if (x->left) x->size += x->left->size; + if (x->right) x->size += x->right->size; + } + return x; + } + node *merge(node *x, node *y) { + if (!x) return y; + if (!y) return x; + if (rand() % (x->size + y->size) < x->size) { + x->right = merge(x->right, y); + return update(x); + } else { + y->left = merge(x, y->left); + return update(y); + } + } + template // 3-way split: cond(x) < 0, cond(x) == 0, cond(x) > 0 + tuple split(node *x, C cond) { + if (!x) return make_tuple(x,x,x); + if (cond(x) == 0) { + auto a = split(x->left, cond); + auto b = split(x->right, cond); + x->left = x->right = 0; update(x); + get<1>(a) = merge(merge(get<1>(a), x), get<1>(b)); + get<2>(a) = get<2>(b); + return a; + } + if (cond(x) < 0) { + auto a = split(x->right, cond); + x->right = 0; update(x); + get<0>(a) = merge(x, get<0>(a)); + return a; + } + if (cond(x) > 0) { + auto a = split(x->left, cond); + x->left = 0; update(x); + get<2>(a) = merge(get<2>(a), x); + return a; + } + } + node *leftmost(node *x) { while (x && x->left) x = x->left; return x; } + node *rightmost(node *x) { while (x && x->right) x = x->right; return x; } + template + void process(node *x, F func) { + if (!x) return; + process(x->left, func); + func(x); + process(x->right, func); + } + + arrangement(vector segs_) : segs(segs_) { + ns.resize(segs.size()); + + set events; + map> L, R; + for (int i = 0; i < segs.size(); ++i) { + if (segs[i].q < segs[i].p) swap(segs[i].p, segs[i].q); + events.insert(segs[i].p); + events.insert(segs[i].q); + L[segs[i].p].insert(i); + R[segs[i].q].insert(i); + ns[i].index = i; + } + vector last(segs.size()); + + while (!events.empty()) { + const point p = *events.begin(); + events.erase(events.begin()); + + Vertex *u = new Vertex({p}); + vertices.push_back(u); + + auto cond = [&](node *x) { + const segment &s = segs[x->index]; + if (sign(s.q.x - s.p.x) == 0) { + if (sign(p.y - s.p.y) < 0) return -1; + if (sign(s.q.y - p.y) < 0) return +1; + return 0; + } + return -sign(cross(s.p - p, s.q - p)); + }; + auto z = split(root, cond); + vector inserter; + process(get<1>(z), [&](node *x) { + Vertex *v = last[x->index]; + if (!adj[u][v]) { + adj[u][v] = new Edge({u}); + adj[v][u] = new Edge({v}); + adj[u][v]->twin = adj[v][u]; + adj[v][u]->twin = adj[u][v]; + edges.push_back(adj[u][v]); + edges.push_back(adj[v][u]); + } + if (!R[p].count(x->index)) + inserter.push_back(x); + }); + for (int i: L[p]) + if (!R[p].count(i)) + inserter.push_back(&ns[i]); + + sort(all(inserter), [&](node *x, node *y) { + const segment &s = segs[x->index], &t = segs[y->index]; + return sign(cross(s.q - s.p, t.q - t.p)) >= 0; + }); + auto add_event = [&](node *x, node *y) { + if (!x || !y) return; + vector ps = intersect(segs[x->index], segs[y->index]); + for (point q: ps) + if (p < q) events.insert(q); + }; + if (inserter.empty()) { + add_event(rightmost(get<0>(z)), leftmost(get<2>(z))); + } else { + add_event(rightmost(get<0>(z)), inserter[0]); + add_event(leftmost(get<2>(z)), inserter.back()); + } + root = 0; + for (int i = 0; i < inserter.size(); ++i) { + last[inserter[i]->index] = u; + inserter[i]->left = inserter[i]->right = 0; + root = merge(root, update(inserter[i])); + } + root = merge(merge(get<0>(z), root), get<2>(z)); + } + for (auto &pp: adj) { + Vertex *u = pp.fst; + vector es; + for (auto z: pp.snd) es.push_back(z.snd); + sort(all(es), [&](Edge *e, Edge *f) { + auto quad = [](point p) { + for (int i = 1; i <= 4; ++i, swap(p.x = -p.x, p.y)) + if (p.x > 0 && p.y >= 0) return i; + return 0; + }; + const point p = e->twin->vertex->p - e->vertex->p; + const point q = f->twin->vertex->p - f->vertex->p; + if (quad(p) != quad(q)) return quad(p) < quad(q); + return sign(cross(p, q)) > 0; + }); + u->edge = es.back(); + for (Edge *e: es) { + u->edge->next = e; + u->edge->next->prev = u->edge; + u->edge = u->edge->next; + } + } + } +}; +void AOJ1226() { + for (int n; ~scanf("%d", &n) && n; ) { + //cout << n << endl; + vector> a(4, vector(n)); + for (int k = 0; k < 4; ++k) + for (int i = 0; i < n; ++i) + scanf("%lf", &a[k][i]); + + vector ss = { + {{0,0},{0,1}}, + {{0,1},{1,1}}, + {{1,1},{1,0}}, + {{1,0},{0,0}} + }; + for (int i = 0; i < n; ++i) { + ss.push_back({{a[0][i],0},{a[1][i],1}}); + ss.push_back({{0,a[2][i]},{1,a[3][i]}}); + } + arrangement arr(ss); + + double result = 0; + set seen; + for (auto *e: arr.edges) { + if (seen.count(e)) continue; + auto *f = e; + double area = 0; + do { + seen.insert(f); + area += cross(f->vertex->p, f->twin->vertex->p); + f = f->twin->prev; + } while (f != e); + result = max(result, area); + } + printf("%.6f\n", result/2); + } +} +void AOJ2448() { + int n; scanf("%d", &n); + vector ps(n); + for (int i = 0; i < n; ++i) + scanf("%lf %lf", &ps[i].x, &ps[i].y); + vector ss; + for (int i = 0; i+1 < n; ++i) + ss.push_back({ps[i], ps[i+1]}); + arrangement arr(ss); + + double result = 0; + set seen; + for (auto *e: arr.edges) { + if (seen.count(e)) continue; + auto *f = e; + double area = 0; + do { + seen.insert(f); + area += cross(f->vertex->p, f->twin->vertex->p); + f = f->twin->prev; + } while (f != e); + if (area > 0) result += area; + } + printf("%.12f\n", result/2); +} + + +//----------------------------------------------------------------------------- +// visibility graph in O(|points|^2 |obstacles|) +// +// Here, the visibility of p and q is: +// 1. p, q in [a,b] ==> visible +// 2. a in (p,q) ==> invisible +// 3. (p,q) intersects (a,b) ==> invisible +// 4. m=(p+q)/2 in int(obs) ==> invisible +// +// [non-verified]; +//----------------------------------------------------------------------------- +struct visibility_graph { + struct edge { + int src, dst; + real weight; + }; + int n; + vector ps; + vector> adj; + visibility_graph(vector ps, vector os) : + n(ps.size()), ps(ps), adj(n) { + auto blocked = [&](point s, point t, polygon &obs) { + bool in = false, on = false; + point m = (s + t) / 2; + for (int i = 0; i < obs.size(); ++i) { + int j = (i+1 == obs.size() ? 0 : i+1); + point a = obs[i], b = obs[j]; + if (!sign(cross(a-s,b-s)) && sign(dot(a-s,b-s)) <= 0 && // s is on [a,b] and t is on [a,b] + !sign(cross(a-t,b-t)) && sign(dot(a-t,b-t)) <= 0) return false; + if (!sign(cross(s-a,t-a)) && sign(dot(s-a,t-a)) < 0) return false; // (s,t) contains a + if (sign(cross(a-s,b-s))*sign(cross(a-t,b-t)) < 0 && // (s,t) intersects (a,b) + sign(cross(s-a,t-a))*sign(cross(s-b,t-b)) < 0) return true; + if (b.y < a.y) swap(a, b); // a is lower + if (a.y <= m.y && m.y < b.y) + if (sign(cross(a-m, b-m)) < 0) in = !in; + } + return in; // midpoint is on obs + }; + for (int i = 0; i < ps.size(); ++i) { + for (int j = i+1, k; j < ps.size(); ++j) { + for (k = 0; k < os.size(); ++k) + if (blocked(ps[i], ps[j], os[k])) break; + if (k == os.size()) { + auto len = norm(ps[i] - ps[j]); + adj[i].push_back({i, j, len}); + adj[j].push_back({j, i, len}); + } + } + } + } +}; +// +// weighted geometric shortest path +// +// find a shortest path on 2D plane, +// where each obstacle is assigned a weight (inverse velocity). +// if weight = INF, it reduced to the standard geometric shortest path problem. +// +// basic algorithm: A* search on implicit visibility graph. +// It performs O(n^2) A* search with dist(u,t) as a heuristics. +// +// on a weighted problem, divide edges to approximate solutions. +// +// reference: +// L. Aleksandrov, A. Maheshwari, and J. -R. Sack (1998): +// Determining approximate shortest paths on weighted polyhedral surfaces. +// +// see aso: http://arxiv.org/pdf/0904.2550.pdf +// +struct geometric_shortest_path { + vector ps; + vector prev, next; + vector> region; + vector weight; + void add_region(vector poly, real w = 1.0/0.0) { + int n = ps.size(), k = poly.size(); + vector obj; + for (int i = 0; i < k; ++i) { + ps.push_back(poly[i]); + obj.push_back(n+i); + next.push_back(n+i+1); + prev.push_back(n+i-1); + } + next[n+k-1] = n; + prev[n] = n+k-1; + region.push_back(obj); + weight.push_back(w); + } + void refine(int v) { + auto divide = [&](int u, int w) { + int v = ps.size(); + ps.push_back((ps[u] + ps[w])/2); + prev.push_back(u); + next.push_back(w); + prev[next[v]] = v; + next[prev[v]] = v; + }; + divide(prev[v], v); + divide(v, next[v]); + } + enum { INTERSECT, CONTAINED, ONLINE, VISIBLE }; + int visibility(point s, point t, vector ®) { + bool in = false; + point m = (s + t) / 2; + for (int i = 0; i < reg.size(); ++i) { + point a = ps[reg[i]], b = ps[reg[(i+1)%reg.size()]]; + if (!sign(cross(a-s,b-s)) && sign(dot(a-s,b-s)) <= 0 && // s is on [a,b] and t is on [a,b] + !sign(cross(a-t,b-t)) && sign(dot(a-t,b-t)) <= 0) return ONLINE; + if (sign(cross(a-s,b-s))*sign(cross(a-t,b-t)) < 0 && // (s,t) intersects (a,b) + sign(cross(s-a,t-a))*sign(cross(s-b,t-b)) < 0) return INTERSECT; + if (b.y < a.y) swap(a, b); + if (a.y <= m.y && m.y < b.y && sign(cross(a-m, b-m)) < 0) in = !in; + } + return in ? CONTAINED : VISIBLE; + } + real length(int u, int v) { + point s = ps[u], t = ps[v]; + auto len = norm(s - t); + for (int i = 0; i < region.size(); ++i) { + int a = visibility(s, t, region[i]); + if (a == ONLINE) return len; + if (a == CONTAINED) return weight[i] * len; + if (a == INTERSECT) return 1.0 / 0.0; + } + return len; + } + pair> shortest_path(point p, point q) { + ps.push_back(p); ps.push_back(q); + int n = ps.size(), s = n-2, t = n-1; + vector dist(n, 1.0/0.0), h(n); dist[s] = 0; + for (int u = 0; u < n; ++u) h[u] = norm(ps[u]-ps[t]); // modify here if weight < 1.0 + vector last(n, ~s); // negative -> unfixed + while (last[t] < 0) { + int u = t; + for (int v = 0; v < n; ++v) + if (last[v] < 0 && dist[v] + h[v] < dist[u] + h[u]) u = v; + last[u] = ~last[u]; + for (int v = 0; v < n; ++v) { + if (last[v] >= 0) continue; + auto len = dist[u] + length(u, v); + if (sign(dist[v] - len) > 0) { + dist[v] = len; + last[v] = ~u; + } + } + } + auto cost = dist[t]; + vector path; + if (cost < 1.0 / 0.0) { + while (t != s) { + path.push_back(t); + t = last[t]; + } + path.push_back(s); + } + ps.pop_back(); ps.pop_back(); + return {cost, path}; + } +}; + +//----------------------------------------------------------------------------- +// merge segments in O(n log n) +// [non-verified] +//----------------------------------------------------------------------------- +vector merge_segments(vector ss) { + auto compare = [&](segment s, segment t) { + int a = sign(cross(s.q-s.p, t.p-t.q)); + return a ? a < 0 : sign(cross(t.p-s.p, t.q-s.p)) < 0; + }; + for (segment &s: ss) if (s.q < s.p) swap(s.p, s.q); + sort(all(ss), compare); + vector res; + for (int i = 0, j; i < ss.size(); i = j) { + for (j = i+1; j < ss.size(); ++j) + if (compare(ss[i], ss[j])) break; + point o = ss[i].p, v = ss[i].q - ss[i].p; + sort(ss.begin()+i, ss.begin()+j, [&](segment s, segment &t) { + auto a = dot(s.p - t.p, v); + if (a) return a < 0; + return dot(s.q - t.q, v) < 0; + }); + for (int a = i, b; a < j; a = b) { + for (b = a+1; b < j; ++b) { + if (sign(dot(ss[a].q - ss[b].p, v)) < 0) break; + if (dot(ss[a].q - ss[b].q, v) < 0) ss[a].q = ss[b].q; + } + res.push_back(ss[a]); + } + } + return res; +} + +//----------------------------------------------------------------------------- +// relative neighborhood graph +// there is an edge between p and q iff +// d(p,q) < d(p,r), and d(p,q) < d(q,r) +// for all other point r. +// +// it contains euclidean mst. +// +// O(n^2) by Katajainen and Nevalainen +// +// [non-verified] +//----------------------------------------------------------------------------- +struct relative_neighborhood_graph { + struct edge { + int src, dst; + real len; + }; + int n; + vector ps; + vector> adj; + relative_neighborhood_graph(vector ps) : + n(ps.size()), ps(ps), adj(n) { + + auto quadrant = [&](point p) { + for (int i = 0; i < 8; ++i) { + if (p.x >= 0 && p.y >= 0 && p.y < p.x) return i; + p = (point){p.x + p.y, -p.x + p.y}; + } + return -1; + }; + for (int i = 0; i < n; ++i) { + vector> cand(8); + vector dist(8, 1.0/0.0); + for (int j = 0; j < n; ++j) { + if (i == j) continue; + int k = quadrant(ps[j] - ps[i]); + real d = norm2(ps[j] - ps[i]); + if (sign(d - dist[k]) < 0) cand[k] = {j}, dist[k] = d; + else if (sign(d - dist[k]) == 0) cand[k].push_back(j); + } + for (int k = 0, l; k < 8; ++k) { + //cout << dist[k] << endl; + for (int j: cand[k]) { + if (j >= i) continue; + for (l = 0; l < n; ++l) { + if (l == i || l == j) continue; + if (sign(dist[k] - norm2(ps[l] - ps[i])) > 0) break; + } + if (l == n) { + adj[i].push_back({i, j, sqrt(dist[k])}); + adj[j].push_back({j, i, sqrt(dist[k])}); + } + } + } + } + } +}; + + +// --------------------------------------------------------------------------- +// maximum area of empty convex k-gon +// +// David P. Dovkin, Harbert Edelsbrunner, and Mark H. Overmars (1990): +// Searching for Empty Convex Polygons. +// Algorithmica, vol.5, no.1, pp.561--571. +// +// Complexity: O(n^3) +// +// [non-verified] +// --------------------------------------------------------------------------- +real maximum_area_empty_convex_polygon(vector ps) { + int n = ps.size(); + real ans = 0; + for (int o = 0; o < n; ++o) { + vector vs; + for (point p: ps) + if (p < ps[o]) vs.push_back(p - ps[o]); + int m = vs.size(); + sort(all(vs), [&](point p, point q) { + int s = sign(cross(p, q)); + return s ? s > 0 : sign(dot(q,q) - dot(p,p)) > 0; + }); + vector> dp(m, vector(m)); + for (int k = 1; k < m; ++k) { + int i = k-1; + while (i >= 0 && !sign(cross(vs[i], vs[k]))) --i; + vector seq; + for (int j; i > 0; i = j) { + seq.push_back(i); + for (j = i-1; j > 0 && sign(cross(vs[i]-vs[k], vs[j]-vs[k])) > 0; --j); + auto v = cross(vs[i], vs[k]); + if (j >= 0) v += dp[i][j]; + dp[k][i] = v; + ans = max(ans, v); + } + for (int i = (int)seq.size()-2; i >= 0; --i) + dp[k][seq[i]] = max(dp[k][seq[i]], dp[k][seq[i+1]]); + } + } + return ans; +} + +//----------------------------------------------------------------------------- +// delaunay triangulation +// O(n^2) in the worst case, O(n log n) -- O(n sqrt(n)) in pratice. +// +// [verified: RUPC 2013 F] +//----------------------------------------------------------------------------- +struct delaunay { + struct edge { + int src, dst; + }; + int n; + vector ps; + vector> adj; // optional + vector inner; + int incircle(int a, int b, int c, int p) { + point u = ps[a]-ps[p], v = ps[b]-ps[p], w = ps[c]-ps[p]; + return sign(norm2(u)*cross(v,w) + +norm2(v)*cross(w,u) + +norm2(w)*cross(u,v)) > 0; + } + bool orient(int a, int b, int p) { + point u = ps[a]-ps[b], v = ps[p]-ps[b]; + int s = sign(cross(u, v)); + return s ? s > 0 : sign(dot(u, v)) > 0; + } + delaunay(vector ps) : n(ps.size()), ps(ps), adj(n), inner(n) { + if (n <= 1) return; + vector> ccw(n); // ccw[u][v] is the third pt for (u,v) + auto make_triangle = [&](int a, int b, int c) { + ccw[a][b] = c; ccw[b][c] = a; ccw[c][a] = b; + }; + vector is(n); iota(all(is), 0); + sort(all(is), [&](int i, int j) { return ps[i] < ps[j]; }); + // delaunay flips + function rec = [&](int a, int b) { + if (!ccw[a].count(b) || !ccw[b].count(a)) return; + int c = ccw[a][b], d = ccw[b][a]; + if (incircle(a, b, c, d) > 0) { + ccw[a].erase(b); ccw[b].erase(a); + make_triangle(d, c, a); + make_triangle(c, d, b); + rec(a, d); rec(d, b); rec(b, c); rec(c, a); + } + }; + // lexicographic triangulation + vector next(n,-1), prev(n,-1); + next[is[0]] = prev[is[0]] = is[1]; + next[is[1]] = prev[is[1]] = is[0]; + for (int i = 2; i < n; ++i) { + int h = is[i], u = is[i-1], v = u; + while ( orient(u, next[u], h)) u = next[u]; + while (!orient(v, prev[v], h)) v = prev[v]; + for (int w = v; w != u; w = next[w]) + if (sign(cross(ps[next[w]]-ps[h], ps[w]-ps[h])) > 0) + make_triangle(w, h, next[w]); + next[h] = u; prev[u] = h; + prev[h] = v; next[v] = h; + } + for (int u: is) { + auto nbh = ccw[u]; // hardcopy + for (auto z: nbh) rec(z.fst, z.snd); // flip + } + // complete graph structure + for (int u: is) { + int v = ccw[u].begin()->fst, s = v; + while (ccw[s].count(u)) { + s = ccw[s][u]; + if (s == v) break; + } // v != s means that u is on the outer face + if (v != s) { inner[u] = false; v = s; } + do { + adj[u].push_back({u, v}); + if (!ccw[u].count(v)) break; + v = ccw[u][v]; + } while (v != s); + } + } +}; +//----------------------------------------------------------------------------- +// voronoi diagram (from delaunay triangulation) +// O(sum deg^2) = O(n^2) in worst case, O(n) for random input. +// +// [verified: RUPC 2013 F] +//----------------------------------------------------------------------------- +struct voronoi { + struct edge { + int src, dst; + real len; + }; + int n, m; + vector ps, qs; // qs is the voronoi vertices + map id; + vector> cell; + vector> adj; + void add_edge(int u, int v) { + auto len = norm(qs[u] - qs[v]); + adj[u].push_back({u, v, len}); + adj[v].push_back({v, u, len}); + } + int node(point p) { + if (!id.count(p)) { id[p] = m++; qs.push_back(p); adj.push_back({}); } + return id[p]; + } + voronoi(delaunay DT, vector domain) : + n(DT.n), m(0), ps(DT.ps), cell(n) { + for (int u = 0; u < n; ++u) { + vector region = domain; + for (auto e: DT.adj[u]) { + point s = (ps[e.src]+ps[e.dst])/2, d = orth(ps[e.dst]-ps[e.src]); + region = convex_cut(region, {s, s+d}); + } + for (int i = 0; i < region.size(); ++i) { + add_edge(node(region[i]), node(region[(i+1)%region.size()])); + cell[u].push_back(node(region[i])); + } + } + } + voronoi(vector ps, vector domain) : + voronoi(delaunay(ps), domain) { } +}; + +/* +DCEL にしたい + +struct Delaunay { + struct Vertex; struct Edge; + struct Vertex { + point p; + Edge *edge; // any adjacent edge + }; + struct Edge { + Vertex *vertex; // origin + Edge *twin; + Edge *prev, *next; + }; + int incircle(Vertex *a, Vertex *b, Vertex *c, Vertex *p) { + point u = a->p - p->p, v = b->p - p->p, w = c->p - p->p; + return sign(norm2(u)*cross(v,w) + +norm2(v)*cross(w,u) + +norm2(w)*cross(u,v)) > 0; + } + bool orient(Vertex *a, Vertex *b, Vertex *p) { + point u = a->p - b->p, v = p->p - b->p; + int s = sign(cross(u, v)); + return s ? s > 0 : sign(dot(u, v)) > 0; + } + Delaunay(vector ps) { + + } + z d w d + / \ / | \ + 枝 a -e-> b ==> a | b + \ / \ v / + y c x c + + を逆転させる手続き + if incircle: + r = e->twin + x = e->next y = e->prev + z = r->next w = r->prev + x->vertex->edge = x + z->vertex->edge = z + w->next = x x->next = r r->next = w + w->prev = r x->prev = w r->prev = x + y->next = z z->next = e e->next = y + y->prev = e z->prev = y e->prev = z + e->vertex = w->vertex + r->vertex = y->vertex + rec(x); rec(y); rec(z); rec(w); + + Vertex *u :: rightmost + Edge *e = u->edge + while orient: e = e->prev + + span(e->next) + e->twin->next で CH を traverse できる + + while !orient: + r = new Edge, s = new Edge, t = new Edge + r->next = s s->next = t t->next = r + r->prev = b + + + + p ---- v + \ / + q / + + auto make_triangle = [&](int a, int b, int c) { + ccw[a][b] = c; ccw[b][c] = a; ccw[c][a] = b; + }; + vector is(n); iota(all(is), 0); + sort(all(is), [&](int i, int j) { return ps[i] < ps[j]; }); + // delaunay flips + function rec = [&](int a, int b) { + if (!ccw[a].count(b) || !ccw[b].count(a)) return; + int c = ccw[a][b], d = ccw[b][a]; + if (incircle(a, b, c, d) > 0) { + ccw[a].erase(b); ccw[b].erase(a); + make_triangle(d, c, a); + make_triangle(c, d, b); + rec(a, d); rec(d, b); rec(b, c); rec(c, a); + } + }; + +}; +*/ + + +//----------------------------------------------------------------------------- +// [non-verified] +//----------------------------------------------------------------------------- +bool is_congruence(polygon ps, polygon qs) { + if (ps.size() != qs.size()) return false; + int n = ps.size(); + vector as, bs; + for (int i = 0; i < n; ++i) { + int j = (i+1 < n ? i+1 : 0), k = (j+1 < n ? j+1 : 0); + as.push_back({dot(ps[i]-ps[j], ps[k]-ps[j]), + cross(ps[i]-ps[j], ps[k]-ps[j])}); + bs.push_back({dot(qs[i]-qs[j], qs[k]-qs[j]), + cross(qs[i]-qs[j], qs[k]-qs[j])}); + } + vector fail(n+1, -1); // knuth morris pratt + for (int i = 1, j = -1; i <= n; ++i) { + while (j >= 0 && !(as[j] == as[i-1])) j = fail[j]; + fail[i] = ++j; + } + for (int i = 0, k = 0; i < 2*n; ++i) { + while (k >= 0 && !(bs[i%n] == as[k])) k = fail[k]; + if (++k == n) return true; // ps[0,n) == qs[i-n+1, *) + } + return false; +} +//----------------------------------------------------------------------------- +// [non-verified] +//----------------------------------------------------------------------------- +bool is_similar(polygon ps, polygon qs) { + if (ps.size() != qs.size()) return false; + int n = ps.size(); + vector as, bs; + for (int i = 0; i < n; ++i) { + int j = (i+1 < n ? i+1 : 0), k = (j+1 < n ? j+1 : 0); + as.push_back({dot(ps[i]-ps[j], ps[k]-ps[j]), + cross(ps[i]-ps[j], ps[k]-ps[j])}); + bs.push_back({dot(qs[i]-qs[j], qs[k]-qs[j]), + cross(qs[i]-qs[j], qs[k]-qs[j])}); + } + real maxa = as[0].x, maxb = bs[0].y; + for (int i = 1; i < n; ++i) + maxa = max(maxa, as[i].x), maxb = max(maxb, bs[i].x); + vector fail(n+1, -1); // knuth morris pratt + for (int i = 1, j = -1; i <= n; ++i) { + while (j >= 0 && !(as[j] == as[i-1])) j = fail[j]; + fail[i] = ++j; + } + for (int i = 0, k = 0; i < 2*n; ++i) { + while (k >= 0 && !(maxa*bs[i%n] == maxb*as[k])) k = fail[k]; + if (++k == n) return true; // maxb * ps[0,n) == maxa * qs[i-n+1, *) + } + return false; +} +//----------------------------------------------------------------------------- +// [non-verified] +//----------------------------------------------------------------------------- +polygon simplify(polygon ps) { + int n = ps.size(); + polygon qs; + for (int i = 0; i < n; ++i) { + int j = (i+1 < n ? i+1 : 0), k = (j+1 < n ? j+1 : 0); + if (sign(dot(ps[j]-ps[i], ps[k]-ps[i])) < 0 || + sign(cross(ps[j]-ps[i], ps[k]-ps[i]))) + qs.push_back(ps[j]); + } + return qs; +} + + +//----------------------------------------------------------------------------- +// VP tree (metric tree) +// +// [non-verified] +//----------------------------------------------------------------------------- +struct vantage_point_tree { + int n; + vector ps; + vector> aux; + vantage_point_tree(vector ps) : n(ps.size()), ps(ps), aux(n) { + for (int i = 0; i < n; ++i) aux[i].snd = i; + function rec = [&](int l, int r) { + if (l == r) return; + swap(aux[l], aux[l+rand()%(r-l)]); + for (int i = l+1; i < r; ++i) + aux[i].fst = norm(ps[aux[i].snd] - ps[aux[l].snd]); + int m = (l+1 + r) / 2; + nth_element(aux.begin()+l+1, aux.begin()+m, aux.begin()+r); + aux[l].fst = aux[m].fst; + rec(l+1, m); rec(m, r); + }; + rec(0, n); + } + vector closest(point p, int k = 1) { + priority_queue> topk; + function rec = [&](int l, int r) { + int m = (l+1 + r) / 2; + if (l == r) return; + auto d = norm(p - ps[aux[l].snd]); + if (topk.size() < k) topk.push({d, aux[l].snd}); + else if (topk.top().fst > d) { + topk.pop(); + topk.push({d, aux[l].snd}); + } + if (d <= aux[l].fst) { + rec(l+1, m); + if (aux[l].fst - d < topk.top().fst) rec(m, r); + } else { + rec(m, r); + if (d - aux[l].fst < topk.top().fst) rec(l+1, m); + } + }; + rec(0, n); + vector ans; + for (; !topk.empty(); topk.pop()) ans.push_back(topk.top().snd); + reverse(all(ans)); + return ans; + } +}; + +//----------------------------------------------------------------------------- +// random ball over +// O(n f(n)) construction, O(n/f(n)) query +// If there are T queries, set f(n) = O(sqrt(T)). +// +// [non-verified] +//----------------------------------------------------------------------------- +struct random_ball_cover { + int n, m; + vector ps, cs; + vector>> vs; + random_ball_cover(vector ps) : n(ps.size()), ps(ps), cs(ps) { + for (int k = m = 1; m < n && k < n; k *= 2, ++m); // m = O(log(n)) + cs.resize(m); vs.resize(m); + for (int u = 0; u < n; ++u) { + int j = 0; + real best = norm(cs[j] - ps[u]); + for (int k = 1; k < m; ++k) { + real len = norm(cs[k] - ps[u]); + if (best > len) { best = len; j = k; } + } + vs[j].push_back({best, u}); + } + for (int j = 0; j < m; ++j) { + sort(all(vs[j])); reverse(all(vs[j])); + } + } + vector closest(point p, int k = 1) { + vector> order(m); + for (int j = 0; j < m; ++j) + order[j] = {norm(cs[j] - p), j}; + sort(all(order)); + priority_queue> topk; + for (auto ord: order) { + real rad = ord.fst; + for (auto z: vs[ord.snd]) { + if (topk.size() < k || topk.top().fst > ord.fst - z.fst) { + topk.push({norm(ps[z.snd] - p), z.snd}); + if (topk.size() > k) topk.pop(); + } else break; + } + } + vector ans; + for (; !topk.empty(); topk.pop()) ans.push_back(topk.top().snd); + reverse(all(ans)); + return ans; + } +}; + + +//----------------------------------------------------------------------------- +// Dynamic Convex Hull (Overmars-Leeuwen) +// +// Memoise divide-and-conquer procedure in segment tree. +// Each tree nodes stores convex hulls by binary search tree. +// +// [verified] CODECHEF MGCHGEOM, ACM/ICPC 2017-D +//----------------------------------------------------------------------------- + +template +struct half_hull { + struct node { + point p; + node *child[2], *parent; + node *next[2]; + + real area; + double perimeter; + }; + node *update(node *t) { + if (t) { + t->area = 0; + if (t->child[0]) t->area += t->child[0]->area + cross(t->p, t->next[0]->p); + if (t->child[1]) t->area += t->child[1]->area - cross(t->p, t->next[1]->p); + + t->perimeter = 0; + if (t->child[0]) t->perimeter += t->child[0]->perimeter + norm(t->p - t->next[0]->p); + if (t->child[1]) t->perimeter += t->child[1]->perimeter + norm(t->p - t->next[1]->p); + } + return t; + } + int dir(node *x, node *y) { return x && x->child[0] == y ? 0 : 1; } + void link(node *x, node *y, int d) { + if (x) x->child[d] = y; + if (y) y->parent = x; + } + void rot(node *x, int d) { + node *y = x->child[d], *z = x->parent; + link(x, y->child[!d], d); + link(y, x, !d); + link(z, y, dir(z, x)); + update(x); update(y); + } + void splay(node *x) { + if (!x) return; + while (x->parent) { + node *y = x->parent, *z = y->parent; + int dy = dir(y, x), dz = dir(z, y); + if (!z) { rot(y, dy); } + else if (dy == dz) { rot(z, dz), rot(y, dy); } + else { rot(y, dy), rot(z, dz); } + } + } + int n; + vector vs; + vector ch, sh; + vector ps; + half_hull(vector ps_) : ps(ps_) { + sort(all(ps)); + ps.erase(unique(all(ps)), ps.end()); + n = ps.size(); + vs.resize(n); + for (int i = 0; i < n; ++i) vs[i] = {ps[i]}; + ch.resize(4*n); sh.resize(4*n); + } + pair bridge(node *lch, node *rch) { + if (lch && rch) { + while (1) { + int it = 0; + for (splay(rch); ; ++it) { + if (rch->next[0] && turn*sign(cross(rch->next[0]->p - lch->p, rch->p - lch->p)) <= 0) { + rch = rch->child[0]; + } else if (rch->next[1] && turn*sign(cross(rch->p - lch->p, rch->next[1]->p - lch->p)) > 0) { + rch = rch->child[1]; + } else break; + } + for (splay(lch); ; ++it) { + if (lch->next[1] && turn*sign(cross(lch->p - rch->p, lch->next[1]->p - rch->p)) <= 0) { + lch = lch->child[1]; + } else if (lch->next[0] && turn*sign(cross(lch->next[0]->p - rch->p, lch->p - rch->p)) > 0) { + lch = lch->child[0]; + } else break; + } + if (it == 0) break; + } + } else if (lch) { + for (splay(lch); lch->child[1]; lch = lch->child[1]); + } else if (rch) { + for (splay(rch); rch->child[0]; rch = rch->child[0]); + } + return {lch, rch}; + } + node *apart(node *x, int d) { + if (!x) return 0; + node *y = x->child[d]; + if (!y) return 0; + y->parent = x->child[d] = 0; + for (; y->child[!d]; y = y->child[!d]); + splay(y); + x->next[d] = y->next[!d] = 0; + update(x); + return y; + } + node *join(node *x, node *y) { + if (!x) return y; + if (!y) return x; + x->child[1] = x->next[1] = y; + y->parent = y->next[0] = x; + update(x); + return x; + } + template + void build(int l, int r, int k, int i) { + if (l+1 == r) { ch[k] = I ? &vs[i] : 0; return; } + splay(ch[2*k+1]); + apart(ch[2*k+1], 1); + ch[2*k+1] = join(ch[2*k+1], sh[2*k+1]); + splay(ch[2*k+2]); + ch[2*k+2] = join(sh[2*k+2], ch[2*k+2]); + if (i < (l+r)/2) build(l, (l+r)/2, 2*k+1, i); + else build((l+r)/2, r, 2*k+2, i); + tie(ch[2*k+1], ch[2*k+2]) = bridge(ch[2*k+1], ch[2*k+2]); + splay(ch[2*k+1]); + splay(ch[2*k+2]); + sh[2*k+1] = apart(ch[2*k+1], 1); + sh[2*k+2] = apart(ch[2*k+2], 0); + ch[k] = join(ch[2*k+1], ch[2*k+2]); + } + int index(point p) const { return lower_bound(all(ps), p) - ps.begin(); } + void insert(point p) { build<1>(0, n, 0, index(p)); } + void erase(point p) { build<0>(0, n, 0, index(p)); } + + node *find(point p) { + node *t = ch[0]; + while (t && t->p != p) + t = t->child[t->p < p]; + return t; + } +}; +struct dynamic_convex_hull { + half_hull<+1> upper; + half_hull<-1> lower; + + dynamic_convex_hull(vector ps) : upper(ps), lower(ps) { } + void insert(point p) { upper.insert(p); lower.insert(p); } + void erase(point p) { upper.erase(p); lower.erase(p); } + + real area() { + real area = 0; + if (upper.ch[0]) area += upper.ch[0]->area; + if (lower.ch[0]) area -= lower.ch[0]->area; + return area; + } + double perimeter() { + double perimeter = 0; + if (upper.ch[0]) perimeter += upper.ch[0]->perimeter; + if (lower.ch[0]) perimeter += lower.ch[0]->perimeter; + return perimeter; + } + + struct hull_iterator { + half_hull<+1>::node *upper_ptr; + half_hull<-1>::node *lower_ptr; + hull_iterator(half_hull<+1>::node *upper_ptr, half_hull<-1>::node *lower_ptr) : + upper_ptr(upper_ptr), lower_ptr(lower_ptr) { } + + bool operator==(hull_iterator it) { + auto is_end = [&](hull_iterator it) { return !it.upper_ptr && !it.lower_ptr; }; + if (is_end(*this) && is_end(it)) return true; + if (is_end(*this) || is_end(it)) return false; + return *(*this) == *it; + } + bool operator!=(hull_iterator it) { return !operator==(it); } + void operator++() { + if (upper_ptr) upper_ptr = upper_ptr->next[1]; + if (!upper_ptr) { + lower_ptr = lower_ptr->next[0]; + if (lower_ptr && !lower_ptr->next[0]) lower_ptr = 0; + } + } + point operator*() { + return upper_ptr ? upper_ptr->p : lower_ptr->p; + } + }; + hull_iterator begin() { + if (!upper.ch[0]) return hull_iterator(0, 0); + auto upper_ptr = upper.ch[0]; + for (; upper_ptr->child[0]; upper_ptr = upper_ptr->child[0]); + auto lower_ptr = lower.ch[0]; + for (; lower_ptr->child[1]; lower_ptr = lower_ptr->child[1]); + return hull_iterator(upper_ptr, lower_ptr); + } + hull_iterator end() { return hull_iterator(0, 0); } + hull_iterator find(point p) { + hull_iterator it = begin(); + it.upper_ptr = upper.find(p); + if (!it.upper_ptr) it.lower_ptr = lower.find(p); + return it; + } + hull_iterator next(hull_iterator it) { + ++it; + if (it == end()) it = begin(); + return it; + } +}; + +int ACMICPC_JP_2017D() { + int n; + scanf("%d", &n); + vector ps; + for (int i = 0; i < n; ++i) { + double x, y; + scanf("%lf %lf", &x, &y); + ps.push_back({x, y}); + } + dynamic_convex_hull hull(ps); + for (int i = 0; i < n; ++i) + hull.insert(ps[i]); + + double per = hull.perimeter(); + double best1 = 0; + { // best non-adjacent + vector> top; + for (auto it = hull.begin(); it != hull.end(); ++it) { + hull.erase(*it); + top.push_back({per - hull.perimeter(), *it}); + hull.insert(*it); + } + sort(all(top)); reverse(all(top)); + for (int i = 0; i < min(4, (int)top.size()); ++i) { + point p = top[i].snd; + for (int j = i+1; j < min(4, (int)top.size()); ++j) { + point q = top[j].snd; + auto it = hull.next(hull.find(p)); + if (*it == q) continue; + it = hull.next(hull.find(q)); + if (*it == p) continue; + best1 = max(best1, top[i].fst + top[j].fst); + } + } + } + double best2 = 0; + { // best consecurive two + for (auto it = hull.begin(); it != hull.end(); ++it) { + auto jt = hull.next(it); + hull.erase(*it); + hull.erase(*jt); + best2 = max(best2, per - hull.perimeter()); + hull.insert(*it); + hull.insert(*jt); + } + } + double best3 = 0; + { // best successive two + for (auto it = hull.begin(); it != hull.end(); ++it) { + auto jt = hull.next(it), kt = hull.next(jt); + hull.erase(*jt); + for (auto lt = it; lt != kt; lt = hull.next(lt)) { + hull.erase(*lt); + best3 = max(best3, per - hull.perimeter()); + hull.insert(*lt); + } + hull.insert(*jt); + } + } + printf("%.5f\n", max({best1, best2, best3})); +} + +//----------------------------------------------------------------------------- +// kd-tree with distance biseparator. +// +// if we construct naively, it tooks O(n^2). +// using candle burning method (by Bentley), it can construct in O(n log n). +// The following implementation is lazy so O(n log^2 n). +//----------------------------------------------------------------------------- +struct split_tree { + struct node { + node *l, *r; + int d; + point h; + int id; + } *root; + int n; + vector ps; + pair ref(pair p) { + return {{p.fst.y, p.fst.x}, p.snd}; + } + split_tree(vector ps) : n(ps.size()), ps(ps) { + set> s[2]; + for (int i = 0; i < n; ++i) { + pair z = {ps[i], i}; + s[0].insert(z); + s[1].insert(ref(z)); + } + function>*)> rec = [&](set> s[]) { + if (s[0].empty()) return (node*)0; + if (s[0].size() == 1) { + int id = s[0].begin()->snd; + return new node({0, 0, -1, ps[id], id}); + } + int d = 0; + if (prev(s[0].end())->fst - s[0].begin()->fst < + prev(s[1].end())->fst - s[1].begin()->fst) d = 1; + auto it = s[d].begin(), jt = prev(s[d].end()); + point h = (jt->fst + it->fst) / 2; // left if smaller than h + while (1) { + if (it == jt || h <= it->fst) break; ++it; + if (it == jt || jt->fst < h) break; --jt; + } + node *l, *r; + set> ss[2]; + if (it->fst < h) { // violate jt side + ++jt; + do { + ss[!d].insert(ref(*jt)); + s[!d].erase(ref(*jt)); + ss[d].insert(*jt); + jt = s[d].erase(jt); + } while (jt != s[d].end()); + r = rec(ss); + l = rec(s); + } else { // violate it side + do { + --it; + ss[!d].insert(ref(*it)); + s[!d].erase(ref(*it)); + ss[d].insert(*it); + it = s[d].erase(it); + } while (it != s[d].begin()); + l = rec(ss); + r = rec(s); + } + return new node({l, r, d, h, -1}); + }; + root = rec(s); + } + + int depth(node *u) { + if (!u) return 0; + return 1+max(depth(u->l), depth(u->r)); + } + void disp(node *u, int tab = 0) { + if (!u) return; + if (u->d < 0) { + cout << string(tab, ' ') << u->h << endl; + return; + } else { + cout << string(tab, ' ') << "split at " << (u->h) << " (" << u->d << ")" << endl; + disp(u->l, tab+2); + disp(u->r, tab+2); + } + } +}; + +// naive. O(n^2) in worst case. or O(n log(largest gap/smallest gap)) +struct split_tree_n { + struct node { + node *l, *r; + int d; + point h; + int id; + } *root; + + int n; + vector ps; + pair ref(pair p) { + return {{p.fst.y, p.fst.x}, p.snd}; + } + split_tree_n(vector ps) : n(ps.size()), ps(ps) { + function)> rec = [&](vector id) { + if (id.size() == 0) return (node*)0; + if (id.size() == 1) { + return new node({0, 0, -1, ps[id[0]], id[0]}); + } + vector xs, ys; + for (int i: id) { + xs.push_back(ps[i].x); + ys.push_back(ps[i].y); + } + sort(all(xs)); + sort(all(ys)); + if (xs.back() - xs[0] > ys.back() - ys[0]) { + real d = (xs.back()+xs[0])/2; + vector ls, rs; + for (int i: id) { + if (ps[i].x < d) ls.push_back(i); + else rs.push_back(i); + } + return new node({rec(ls), rec(rs), 0, {d,0}, -1}); + } else { + real d = (ys.back()+ys[0])/2; + vector ls, rs; + for (int i: id) { + if (ps[i].y < d) ls.push_back(i); + else rs.push_back(i); + } + return new node({rec(ls), rec(rs), 1, {0,d}, -1}); + } + }; + vector id(n); iota(all(id), 0); + root = rec(id); + } + + int depth(node *u) { + if (!u) return 0; + return 1+max(depth(u->l), depth(u->r)); + } + void disp(node *u, int tab = 0) { + if (!u) return; + if (u->d < 0) { + cout << string(tab, ' ') << u->h << endl; + return; + } else { + cout << string(tab, ' ') << "split at " << (u->h) << " (" << u->d << ")" << endl; + disp(u->l, tab+2); + disp(u->r, tab+2); + } + } +}; + +// TODO +// https://graphics.stanford.edu/courses/cs468-06-fall/Papers/02%20vladlen%20notes.pdf +// epsilon net +// Let (X, F) be a set system. N \subset X is called epsilon net if +// N cat S \neq empty for all S in F with u(S) > epsilon. +// (if S in F has area greater than epsilon, S intersect with N). +// +// Theorem. there exists e-net of size O(d (1/e) log(1/e)) +// + + + +// vor(i) is the polygon such that for all p in vor(i) +// ps[i] is the farthest point from p. +// +// clearly: vor(i) is non-empty if ps[i] is on the convex hull. +// +// TODO +struct farthest_voronoi { + int n; + vector ps, qs; + map id; + /* + int node(point p) { + if (!id.count(p)) { id[p] = m++; qs.push_back(p); adj.push_back({}); } + return id[p]; + } + */ + vector> vor; + + farthest_voronoi(vector ps, vector domain) : + n(ps.size()), ps(ps) { + vector id(n); iota(all(id), 0); + sort(all(id), [&](int i, int j) { return ps[i] < ps[j]; }); + + vector ch(2*n); + auto cond = [&](int i, int j, int k) { + return sign(cross(ps[i]-ps[k], ps[j]-ps[k])) < 0; + }; + int k = 0; + for (int i = 0; i < n; ch[k++] = i++) + for (; k >= 2 && cond(ch[k-2], ch[k-1], i); --k); + for (int i = n-2, t = k+1; i >= 0; ch[k++] = i--) + for (; k >= t && cond(ch[k-2], ch[k-1], i); --k); + ch.resize(k-1); + + cout << ch << endl; + + vector prev(n, -1), next(n, -1); + for (int i = 0; i < ch.size(); ++i) { + int j = i + 1; if (j >= ch.size()) j = 0; + next[ch[i]] = ch[j]; + prev[ch[j]] = ch[i]; + } + + auto disp = [&](int s) { + int u = s; + do { + cout << u << " "; + u = next[u]; + } while (u != s); + cout << endl; + }; + shuffle(all(ch), mt19937()); + vor.resize(n); + for (int i = (int)ch.size()-1; i >= 1; --i) { + disp(ch[0]); + next[prev[ch[i]]] = next[ch[i]]; + prev[next[ch[i]]] = prev[ch[i]]; + } + // つらい + // 基本 DCEL がらみはしんどい + vor[ch[0]] = domain; + cout << "---" << endl; + for (int i = 1; i < ch.size(); ++i) { + next[prev[ch[i]]] = ch[i]; + prev[next[ch[i]]] = ch[i]; + disp(ch[0]); + // ch[i] と next[ch[i]] の bisector で切る + // 領域を hyperplane で管理するのが良いのか + /* + for (int j = 0; j < i; ++j) { + point s = (ps[e.src]+ps[e.dst])/2, d = orth(ps[e.dst]-ps[e.src]); + region = convex_cut(region, {s, s+d}); + } + */ + } + + // ランダムに挿入しながら構築する. + // 点 i を挿入するとき.next[i] の voronoi region を i, next[i] の bisector で切る + // bisector があたる領域を j の voronoi region として i, j の bisector で切る + // これを繰り返す + // + // データ構造処理がデス辛い + // + // 必要な演算 + // voronoi region の辺集合 + + } + +}; + +/* +struct doubly_connected_edge_list { + struct Vertex; + struct Edge; + struct Face; + struct vertex { + Edge *edge; // any incident edge + point p; // + }; + struct Edge { + Vertex *vertex; // origin + Edge *prev, *next; // surrounding edges of face + Edge *twin; // reverse edge of e + Face *face; // left face + }; + struct Face { + Edge *edge; // Any incident edge + }; + + // process edges incident to v in CCW order + void incident(Vertex *v) { + Edge *e = vertex->edge; + do { + // process e + e = e->prev->twin; + } while (e != v->edge) + v->edge; + } + // process vertices incident to e in SRC-DST order + void incident(Edge *e) { + // process e->vertex; + // process e->twin->vertex; + } + // process edges incident to f in CCW order + void incident(Face *f) { + Edge *e = f->edge; + do { + // process e + e = e->next; + } while (e != f->edge); + } +}; +*/ + +// +// Polyhedral Region Overlay +// +struct DCEL { + struct Vertex; + struct Edge; + struct Vertex { + point p; + Edge *edge; + }; + struct Edge { + Vertex *vertex; + Edge *twin; + Edge *prev, *next; + // Face *face; + }; + + DCEL copy() { + + } +}; +void overlay(DCEL *a, DCEL *b) { + // + // a と b で segment intersection をとく + // 各 intersection について新しい点をおいて edge を分割 + // その後,face を assign する + // +}; + + +// Convex Hull of circles; めっちゃバグってる +// +// divide and conquer?? +// +// 2つの凸包から,それらの union の凸包を作ればいい +// +// 最も右端にある円を選び,真上への接直線を引く +// dominant なほうを追加する +// +// TODO +// http://ac.els-cdn.com/092577219290015K/1-s2.0-092577219290015K-main.pdf?_tid=f2321600-62e3-11e6-aa7a-00000aacb35e&acdnat=1471264364_f7d8c0796b081532ec0e84e22028f111 +// +// [non-verified; in progress] +vector convex_hull(vector cs) { + int n = cs.size(); + typedef vector hull; + function merge = [&](hull P, hull Q) { + cout << "---" << endl; + cout << "merge "; + for (auto i: P) cout << "[" << cs[i].p << "," << cs[i].r << "] "; + cout << "and"; + for (auto j: Q) cout << " [" << cs[j].p << "," << cs[j].r << "]"; + cout << endl; + hull S; + unordered_set elem; + auto add = [&](int k) { + if (elem.count(k)) return; + cout << "add [" << cs[k].p << "," << cs[k].r << "]" << endl; + S.push_back(k); + elem.insert(k); + }; + auto supp = [&](int k, point v) { + circle c = cs[k]; + return c.p - c.r * orth(v); + }; + auto dom = [&](int i, int j, point v) { + point p = supp(P[i], v), q = supp(Q[j], v); + cout << "p = " << p << ", q = " << q << endl; + int s = sign(cross(p - q, v)); + cout << "s = " << s << endl; + return s ? s > 0 : sign(dot(q - p, v)) > 0; + }; + auto tangent = [&](int k, int l) { + if (k == l) return point({0,0}); + auto u = cs[l].p - cs[k].p; + auto b = cs[k].r - cs[l].r, g = norm(u); + u /= g; + auto h = b / g; + cout << h << endl; + if (sign(1 - h*h) < 0) return point({0,0}); + return orth(u*h - orth(u)*sqrt(max(0.0l, 1 - h*h))); + }; + auto compare = [&](point a, point b, point v) { + cout << "compare " << a << " " << b << " " << v << endl; + if (sign(dot(a,a)) == 0) return false; + if (sign(dot(b,b)) == 0) return true; + // TODO: v と a, b は同じ方向ではいけない(大丈夫?) + // a はゼロかもしれない + int s = sign(cross(v, a)), t = sign(cross(v, b)); + cout << "s = " << s << ", t = " << t << endl; + if (s == 0 && dot(v, a) > 0) return true; + if (t == 0 && dot(v, b) > 0) return false; + return s != t ? s > t : sign(cross(a, b)) > 0; + }; + auto advance = [&](int &i, int &j, point &v) { + int I = (i+1) % P.size(), J = (j+1) % Q.size(); + point a = tangent(P[i], Q[j]), b = tangent(P[i], P[I]), + c = tangent(Q[j], Q[J]), d = tangent(Q[j], P[i]); + cout << a << " " << b << " " << c << " " << d << endl; + if (compare(a, b, v) && compare(a, c, v)) { cout << a << " is the first turn" << endl; add(Q[j]); } + if (compare(d, b, v) && compare(d, c, v)) { cout << d << " is the first turn" << endl; add(P[i]); } + if (compare(b, c, v)) { v = b; i = I; } + else { v = c; j = J; } + }; + int i = 0, j = 0; + point v = {1, 0}; + for (int iter = 0; iter < 3+P.size()+Q.size(); ++iter) { + if (sign(norm(v)) == 0) break; + cout << "currently " << cs[P[i]].p << " and " << cs[Q[j]].p << endl; + if (dom(i, j, v)) { + cout << cs[P[i]].p << " is the lowermost" << endl; + add(P[i]); + } else { + cout << cs[Q[j]].p << " is the lowermost" << endl; + add(Q[j]); + } + advance(i, j, v); + } + cout << "==> "; + for (auto i: S) cout << "[" << cs[i].p << "," << cs[i].r << "] "; + cout << endl; + return S; + }; + function rec = [&](int l, int r) { + if (l+1 == r) return (hull){l}; + auto P = rec(l, (l+r)/2), Q = rec((l+r)/2, r); + return merge(P, Q); + }; + auto ch = rec(0, n); + vector res; + for (int i: ch) res.push_back(cs[i]); + return res; +} + +void verify_convex_hull_discs() { + int n = 20; + vector cs; + for (int i = 0; i < n; ++i) + cs.push_back({ {urand(), urand()}, urand()/3 }); + auto ch = convex_hull(cs); + + visualizer vis; + for (auto c: cs) vis << c; + point shift = point({vis.maxx+1, 0}); + for (auto c: ch) vis << circle({c.p + shift, c.r}); +} + +void verify_farthest_voronoi() { + + vector ps = { + {0,0}, + {2,0}, + {1,1}, + {2,2}, + {0,2}, + }; + vector region = { + {-10,-10}, + { 10,-10}, + { 10, 10}, + {-10, 10} + }; + farthest_voronoi V(ps, region); +} + + +// verify +// +void verify_delaunay() { + { + vector ps = { + //{0,0}, {1,0}, {1,1}, + /* + {0,0}, {0,1}, {0,2}, + {1,0}, {1,1}, + */ + /* + {0,0}, {1,0}, {2,0}, + {0,1}, {1,1}, {2,1}, + {0,2}, {1,2}, {2,2}, + */ + }; + } + int n = 100000; + vector ps(n); + for (int i = 0; i < n; ++i) + ps[i].x = urand(), ps[i].y = urand(); + tick(); + delaunay DT(ps); + cout << tick() << endl; + + visualizer vis; + for (point p: ps) vis << p; + for (int u = 0; u < DT.n; ++u) { + for (auto e: DT.adj[u]) + vis << segment({ps[e.src], ps[e.dst]}); + } + /* + for (int n = 5; n < 7; n *= 1.5) { + vector ps(n); + for (int i = 0; i < n; ++i) + ps[i].x = 100 * urand(), ps[i].y = 100 * urand(); + delaunay DT(ps); + } + */ +} +void verify_voronoi() { + /* + vector ps = { + {0,0}, {1,0}, {0,1} + }; + */ + for (int n = 5; n < 100000; n *= 1.5) { + vector ps(n); + for (int i = 0; i < n; ++i) + ps[i].x = urand(), ps[i].y = urand(); + + vector region = { + {-0.1,-0.1}, + { 1.1,-0.1}, + { 1.1, 1.1}, + {-0.1, 1.1} + }; + cout << "n = " << n << " "; + voronoi Vor(ps, region); + + visualizer vis; + for (point p: ps) vis << p; + for (int u = 0; u < Vor.m; ++u) { + //vis << Vor.qs[u]; + for (auto e: Vor.adj[u]) + vis << segment({Vor.qs[e.src], Vor.qs[e.dst]}); + } + } +} + +void POJ2235() { + for (int n; ~scanf("%d", &n); ) { + vector ps(n); + for (int i = 0; i < n; ++i) + scanf("%lf %lf", &ps[i].x, &ps[i].y); + delaunay DT(ps); + } +} +void AOJ1514() { + for (int n, m; scanf("%d %d", &n, &m); ) { + if (n == 0) break; + vector ps(n); + for (int i = 0; i < n; ++i) + scanf("%lf %lf", &ps[i].x, &ps[i].y); + delaunay DT(ps); + vector score(n); + double ans = 0.0; + for (int j = 0; j < m; ++j) { + point c, d; + double s; + scanf("%lf %lf %lf %lf %lf", &c.x, &c.y, &d.x, &d.y, &s); + vector domain = { + c + point({-d.x, -d.y}), + c + point({+d.x, -d.y}), + c + point({+d.x, +d.y}), + c + point({-d.x, +d.y}), + }; + voronoi V(DT, domain); + visualizer vis; + for (point p: ps) vis << p; + for (int u = 0; u < V.m; ++u) { + for (auto e: V.adj[u]) + vis << segment({V.qs[e.src], V.qs[e.dst]}); + } + vector &qs = V.qs; + for (int i = 0; i < n; ++i) { + real area = 0; + int K = V.cell[i].size(); + for (int k = 0; k < K; ++k) + area += cross(qs[k], qs[(k+1)%K]); + score[i] += s * area / 2 / (4 * d.x * d.y); + ans = max(ans, score[i]); + } + } + printf("%.12lf\n", ans); + } +} + +void verify_rectangle_union() { + vector rs = { + { {0,0},{2,2} }, + { {1,1},{4,2} }, + { {3,0},{5,2} }, + }; + cout << rectangle_union(rs) << endl; +} + +void verify_relative_neighborhood_graph() { + vector ps = { + {0,0}, + {1,0}, + {0,1}, + {1,1} + }; + relative_neighborhood_graph rng(ps); + for (int u = 0; u < rng.n; ++u) { + for (auto e: rng.adj[u]) { + cout << ps[e.src] << " " << ps[e.dst] << endl; + } + } +} + + +void verify_nearest_neighbor_structure() { + int n = 1000000, m = 100, k = 10; + vector ps(n); + for (int i = 0; i < n; ++i) { + ps[i].x = urand(); + ps[i].y = urand(); + } + tick(); + vantage_point_tree VPT(ps); + cout << "vantage point tree: " << tick() << endl; + random_ball_cover RBC(ps); + cout << "random ball cover: " << tick() << endl; + + double t1 = 0, t2 = 0; + for (int i = 0; i < m; ++i) { + point p; + p.x = urand(); + p.y = urand(); + + cout << "---" << endl; + tick(); + cout << VPT.closest(p, k) << endl; + t1 += tick(); + cout << RBC.closest(p, k) << endl; + t2 += tick(); + } + cout << "vantage point tree: " << t1 << endl; + cout << "random ball over: " << t2 << endl; +} + + + +void verify_points_counter() { + vector ps = { + {0,1}, + {1,0}, + {1,1}, + {2,1}, + }; + points_counter T(ps); +} + + + + + + + + + +void verify_intersectCC() { + auto th = 3.141592653589*rand()/(1.0+RAND_MAX); + auto dx = 1 - 2*rand()/(1.0+RAND_MAX); + auto dy = 1 - 2*rand()/(1.0+RAND_MAX); + auto S = [&](point p) { + real c = cos(th), s = sin(th); + p.x -= dx; p.y -= dy; + return (point){c * p.x + s * p.y, -s * p.x + c*p.y}; + }; + auto T = [&](circle C) { + return (circle){ S(C.p), C.r }; + }; + { + circle C = { {0,0}, 1 }; + circle D = { {2,0}, 1 }; + TEST(intersect(T(C), T(D)).size(), 1); // touch outer + } + { + circle C = { {0,0}, 1 }; + circle D = { {1,0}, 1 }; + TEST(intersect(T(C), T(D)).size(), 2); // properly intersect inner + } + { + circle C = { {0,0}, 2 }; + circle D = { {1,0}, 1 }; + TEST(intersect(T(C), T(D)).size(), 1); // intersect inner + } + { + circle C = { {0,0}, 2 }; + circle D = { {3,0}, 2 }; + TEST(intersect(T(C), T(D)).size(), 2); // properly intersect outer + } + { + circle C = { {0,0}, 3 }; + circle D = { {1,0}, 1 }; + TEST(intersect(T(C), T(D)).size(), 0); // too close + } + { + circle C = { {0,0}, 1 }; + circle D = { {3,0}, 1 }; + TEST(intersect(T(C), T(D)).size(), 0); // too far + } +} + +void verify_three_point_circle() { + for (int iter = 0; iter < 100; ++iter) { + point p = {urand(), urand()}; + point q = {urand(), urand()}; + point r = {urand(), urand()}; + circle c = three_point_circle(p, q, r); + TEST(dot(c.p - p, c.p - p), dot(c.p - q, c.p - q)); + TEST(dot(c.p - p, c.p - p), dot(c.p - r, c.p - r)); + } +} + +void verify_triangulate() { + vector ps; + ps.push_back({0, 0}); + ps.push_back({2, 0}); + ps.push_back({2, 2}); + ps.push_back({1, 1}); + ps.push_back({0, 2}); + TEST(triangulate(ps), area(ps)); +} + +void verify_convex_cut() { + int n; + scanf("%d", &n); + vector ps(n); + for (int i = 0; i < n; ++i) { + scanf("%lf %lf", &ps[i].x, &ps[i].y); + } + int m; + scanf("%d", &m); + for (int i = 0; i < m; ++i) { + line L; + scanf("%lf %lf %lf %lf", &L.p.x, &L.p.y, &L.q.x, &L.q.y); + vector qs = convex_cut(ps, L); + printf("%f\n", area(qs)); + } +} +void verify_convex_cut2() { + vector ps = { + {0.,0.}, + {1.,0.}, + {2.,0.}, + {2.,1.}, + {2.,2.}, + {1.,2.}, + {0.,2.}, + {0.,1.} + }; + line L = {{1,0},{1.5,0}}; + vector qs = convex_cut(ps, L); + for (auto q: qs) cout << q << " "; cout << endl; +} +void verify_tangent() { + point p; + circle c; + scanf("%lf %lf", &p.x, &p.y); + scanf("%lf %lf %lf", &c.p.x, &c.p.y, &c.r); + vector ps; + for (auto L: tangent(c, p)) { + if (L.p == p) ps.push_back(L.q); + else ps.push_back(L.p); + } + sort(all(ps)); + for (auto p: ps) + printf("%.12f %.12f\n", p.x + EPS, p.y + EPS); +} +void verify_tangentCC() { + TEST(tangent({{-2.0, 0.0}, 2.0}, {{-2.0, 0.0}, 1.0}).size(), 0); + TEST(tangent({{-2.0, 0.0}, 2.0}, {{-1.0, 0.0}, 1.0}).size(), 1); + TEST(tangent({{-2.0, 0.0}, 2.0}, {{-0.5, 0.0}, 1.0}).size(), 2); + TEST(tangent({{-2.0, 0.0}, 2.0}, {{+1.0, 0.0}, 1.0}).size(), 3); + TEST(tangent({{-2.0, 0.0}, 2.0}, {{+2.0, 0.0}, 1.0}).size(), 4); + TEST(tangent({{+2.0, 0.0}, 2.0}, {{+2.0, 0.0}, 1.0}).size(), 0); + TEST(tangent({{+2.0, 0.0}, 2.0}, {{+1.0, 0.0}, 1.0}).size(), 1); + TEST(tangent({{+2.0, 0.0}, 2.0}, {{+0.5, 0.0}, 1.0}).size(), 2); + TEST(tangent({{+2.0, 0.0}, 2.0}, {{-1.0, 0.0}, 1.0}).size(), 3); + TEST(tangent({{+2.0, 0.0}, 2.0}, {{-2.0, 0.0}, 1.0}).size(), 4); + + for (int iter = 0; iter < 100; ++iter) { + circle c = {{urand(), urand()}, urand()}; + circle d = {{urand(), urand()}, urand()}; + for (line l: tangent(c, d)) { + TEST(intersect(l, c).size(), 1); + TEST(intersect(l, d).size(), 1); + } + } +} +void verify_tangentCC2() { + circle c, d; + cin >> c.p.x >> c.p.y >> c.r; + cin >> d.p.x >> d.p.y >> d.r; + vector ls = tangent(c, d); + vector ps; + for (line l: ls) { + ps.push_back(intersect(l, c)[0]); + } + sort(all(ps)); + for (point p: ps) { + double x = p.x; if (sign(x) == 0) x = 0; + double y = p.y; if (sign(y) == 0) y = 0; + printf("%.12f %.12f\n", x, y); + } +} +void verify_intersect_area() { + vector ps = { + {0.,0.}, + {3.,0.}, + {0.,3.}, + }; + circle c = {{0.0,0.0},2.8}; + cout << intersection_area(ps, c) << endl; +} + +void verify_closest_pair() { + double t1 = 0, t2 = 0; + for (int seed = 3; seed < 100; ++seed) { + srand(seed); + int n = 20000; + vector ps; + for (int i = 0; i < n; ++i) { + ps.push_back({urand(), urand()}); + } + tick(); + auto uv = closest_pair(ps); + t1 += tick(); + auto wx = closest_pair2(ps); + t2 += tick(); + auto d1 = dist(uv.fst, uv.snd); + auto d2 = dist(wx.fst, wx.snd); + cout << uv.fst << " " << uv.snd << " " << dist(uv.fst, uv.snd) << endl; + cout << wx.fst << " " << wx.snd << " " << dist(wx.fst, wx.snd) << endl; + cout << endl; + if (sign(d1 - d2) != 0) { + cout << seed << endl; + break; + } + } + cout << t1 << " " << t2 << endl; +} + +vector half_plane_intersection_n(vector ls) { + real s = 99999999; + vector ps = {{-s,-s},{s,-s},{s,s},{-s,s}}; + for (int i = 0; i < ls.size(); ++i) + ps = convex_cut(ps, ls[i]); + return ps; +} +void verify_half_plane_intersection() { + for (int seed = 0; seed < 1000; ++seed) { + srand(seed); + int n = 3 + rand() % 100; + vector ls; + for (int i = 0; i < n; ++i) { + double th1 = 2 * PI * i / n; + double th2 = 2 * PI * (i+1) / n; + double r1 = 10 + 100 * urand(); + double r2 = 10 + 100 * urand(); + ls.push_back({{r1*cos(th1), r1*sin(th1)}, + {r2*cos(th2), r2*sin(th2)}}); + } + auto a1 = area(half_plane_intersection(ls)); + auto a2 = area(half_plane_intersection_n(ls)); + if (sign(1 - a2/a1)) { cout << a1 << " " << a2 << " " << 1 - a2/a1 << " " << sign(1-a2/a1) << seed << endl; exit(-1); } + } +} + +// TEST +void angular_sort_n(vector &ps) { + vector> qs; + for (point p: ps) { + qs.push_back(make_tuple(arg(p), norm2(p), p)); + if (get<0>(qs.back()) < 0) get<0>(qs.back()) += 2 * PI; + } + sort(all(qs)); + for (int i = 0; i < ps.size(); ++i) + ps[i] = get<2>(qs[i]); +} +void verify_polar_angle() { + double t1 = 0, t2 = 0; + int n = 1000000; + vector ps; + for (int i = 0; i < n; ++i) + ps.push_back({(double)(rand()%2001)-1000, (double)(rand()%2001)-1000}); + vector qs = ps; + tick(); + sort(all(ps), polar_angle()); + t1 += tick(); + angular_sort_n(qs); + t2 += tick(); + for (int i = 0; i < ps.size(); ++i) { + if (!(ps[i] == qs[i])) { cout << "*"; } + //cout << ps[i] << " " << qs[i] << endl; + } + cout << t1 << " " << t2 << endl; +} + +void verify_maximum_circle_cover() { + int n = 10000; + vector ps; + for (int i = 0; i < n; ++i) { + double x = rand() % 1000, y = rand() % 1000; + ps.push_back({x, y}); + } + tick(); + cout << maximum_circle_cover(ps, 20) << endl; + cout << tick() << endl; + cout << maximum_circle_cover2(ps, 20) << endl; + cout << tick() << endl; +} +void verify_maximum_circle_cover2() { + for (int n; scanf("%d", &n) && n; ) { + vector ps(n); + for (int i = 0; i < n; ++i) + scanf("%lf %lf", &ps[i].x, &ps[i].y); + printf("%d\n", maximum_circle_cover2(ps, 1.0)); + } +} + + +void verify_circle_intersection_area() { + circle c = {{0,0}, 2}; + circle d = {{2.8,0}, 1}; + cout << intersection_area(c, d) << endl; + + // Monte-Carlo integration + real w = 10.0, vol = w*w; // w times w box centered at (0,0) + int count = 0, maxcount = 100000000; + for (int i = 0; i < maxcount; ++i) { + point p = {w*urand() - w/2, w*urand() - w/2}; + if (contains(c, p) && contains(d, p)) ++count; + } + real r = 1.0*count/maxcount; + real mu = vol * r, sigma = vol*sqrt((r-r*r)/maxcount); + cout << "95\% confidence: [" << mu-3*sigma << ", " << mu+3*sigma << "]" << endl; +} + +void verify_geometric_median() { + int n = 10; + vector ps(n); + for (int i = 0; i < n; ++i) { + ps[i].x = urand(); + ps[i].y = urand(); + } + cout << geometric_median(ps) << endl; +} +void verify_geometric_median2() { + while (1) { + vector ps(4); + for (int i = 0; i < 4; ++i) + cin >> ps[i].x >> ps[i].y; + if (ps[0].x < 0) break; + point g = geometric_median(ps); + //cout << g << endl; + double w = 0; + for (int i = 0; i < 4; ++i) + w += norm(ps[i] - g); + printf("%.4f\n", w); + } +} +void verify_tangent_circles() { + line l = { {0,0},{1,0} }; + line m = { {0,2},{1,2} }; + for (circle c: tangent_circles(l, m, 1.0)) { + cout << c.p << " " << c.r << endl; + cout << " "; for (point p: intersect(c, l)) { cout << p << " "; } cout << endl; + cout << " "; for (point p: intersect(c, m)) { cout << p << " "; } cout << endl; + } +} + + +void verify_arrangement() { + vector ss = { + {{0,0},{2,0}}, + {{3,0},{5,0}}, + {{1,0},{4,0}} + }; + arrangement arr(ss); +} + +void verify_is_congruence() { + int n = 10; + vector ps(n); + for (int i = 0; i < n; ++i) { + ps[i] = {urand(), urand()}; + } + double r = urand(); + point u = {urand(), urand()}; + double c = urand(), s = sqrt(1 - c*c); + vector qs; + for (int i = 0; i < ps.size(); ++i) { + qs.push_back(r * (point){c*ps[i].x+s*ps[i].y, -s*ps[i].x+c*ps[i].y} + u); + } + int k = rand() % qs.size(); + rotate(qs.begin(), qs.begin()+k, qs.end()); + cout << is_congruence(ps, qs) << endl; + cout << is_similar(ps, qs) << endl; +} + + +void verify_merge_segment() { + vector ss = { + {{0,0},{2,0}}, + {{0,0},{0,2}}, + {{4,0},{7,0}}, + {{5,0},{6,0}}, + }; + merge_segments(ss); +} +void verify_farthest_pair() { + int n = 30; + vector ps(n); + for (int i = 0; i < n; ++i) { + ps[i].x = rand() % 10; + ps[i].y = rand() % 10; + } + auto ans = farthest_pair(ps); + + real best = -1; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + best = max(best, norm(ps[i]-ps[j])); + } + } + + cout << norm(ans.fst-ans.snd) << " " << best << endl; +} + +void ACAC002() { + int n; cin >> n; + vector ps(n); + for (int i = 0; i < n; ++i) + cin >> ps[i]; + auto ans = farthest_pair(ps); + printf("%.12f\n", norm(ans.snd-ans.fst)); +} + +void verify_split_tree() { + for (int n = 1; n < 1000000; n *= 2) { + vector ps; + double x = 1, y = 0; + for (int i = 0; i < n; ++i) { + x *= 1.001; + y = 0; //rand() / (RAND_MAX +1.0); + ps.push_back({x,y}); + } + sort(all(ps)); ps.erase(unique(all(ps)), ps.end()); + tick(); + split_tree T(ps); + cout << T.depth(T.root) << endl; + cout << n << " " << tick() << endl; + } +} + + +int main() { + //verify_intersectCC(); + //verify_three_point_circle(); + //verify_triangulate(); + //verify_convex_cut2(); + //verify_tangent(); + //verify_tangentCC(); + //verify_tangentCC2(); + //verify_intersect_area(); + //verify_closest_pair(); + //verify_half_plane_intersection(); + //verify_polar_angle(); + //verify_maximum_circle_cover(); + //verify_maximum_circle_cover2(); + //verify_rectangle_union(); + //verify_points_counter(); + //verify_circle_intersection_area(); + //verify_geometric_median(); + //verify_tangent_circles(); + //verify_maximum_points_line(); + //verify_arrangement(); + //verify_merge_segment(); + //AOJ1226(); + //AOJ2448(); + //AOJ1247(); + //verify_relative_neighborhood_graph(); + //verify_nearest_neighbor_structure(); + //verify_delaunay(); + //POJ2235(); + // verify_voronoi(); + //verify_is_congruence(); + //AOJ1514(); + //AOJ0273(); + //verify_farthest_pair(); + //ACAC002(); + //verify_compressed_quad_tree(); + //verify_split_tree(); + //verify_farthest_voronoi(); + //verify_convex_hull_discs(); +} From 68d7c6e53e0f143580989ba23e406c5844fce8af Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 30 Dec 2017 11:41:03 +0900 Subject: [PATCH 02/80] order maintenance data structure (dietz and sleator) --- data_structure/order_maintenance.cc | 122 ++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 data_structure/order_maintenance.cc diff --git a/data_structure/order_maintenance.cc b/data_structure/order_maintenance.cc new file mode 100644 index 0000000..4555199 --- /dev/null +++ b/data_structure/order_maintenance.cc @@ -0,0 +1,122 @@ +// +// Order Maintenance +// +// - create_node(): return new node x +// - insert(x, y): insert node y after node x +// - erase(x): erase node x from the list +// - order(x, y): return true if x is before y +// +// Running Time: +// worst case O(1) for create_node, erase, and order. +// amortized O(log n) for insert; very small constant. +// +// Reference: +// P. Dietz and D. Sleator (1987): +// "Two algorithms for maintaining order in a list". +// STOC. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define fst first +#define snd second +#define all(c) (c).begin(), (c).end() + +using namespace std; + + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} + +struct order_maintenance { + using label_type = unsigned long long; + using signed_label_type = long long; + const label_type M = ~((~label_type(0))>>1); + struct node { + node *prev = 0, *next = 0; + label_type label; // allows to contain at most sqrt(M) elements + } *head; + + order_maintenance() { + head = new node(); + head->next = head->prev = head; + } + label_type width(node *x, node *y) { + label_type ret = y->label - x->label; + if (ret - 1 >= M) ret += M; + return ret; + } + void insert(node *x, node *u) { + label_type label = x->label; + if (width(x, x->next) <= 1) { + node *mid = x->next, *end = mid->next; + label_type required = 3; + while (width(x, end) <= 4 * width(x, mid) && end != x) { + ++required; + end = end->next; + if (end == x) break; + ++required; + end = end->next; + mid = mid->next; + } + label_type gap = (x == end ? M : width(x, end)) / required; + label_type val = end->label; + while (1) { + if (end == head) val += M; + end = end->prev; + if (end == x) break; + val -= gap; + end->label = val; + } + } + u->label = label + width(x, x->next)/2; + u->next = x->next; + u->prev = x; + u->next->prev = u; + u->prev->next = u; + } + void erase(node *u) { + u->prev->next = u->next->prev; + u->next->prev = u->prev->next; + } + node *create_node() { + return new node; + } + bool order(node *x, node *y) { + return x->label < y->label; + } +}; + +int main() { + order_maintenance T; + int n = 1000000; + vector nodes(n); + vector order(n); + for (int i = 0; i < n; ++i) { + nodes[i] = T.create_node(); + order[i] = i; + } + random_shuffle(all(order)); + tick(); + T.insert(T.head, nodes[order[0]]); + for (int i = 1; i < n; ++i) { + //cout << i << endl; + T.insert(nodes[order[i-1]], nodes[order[i]]); + } + cout << tick() << endl; +} From 846d2dffcdc588c61e074988ac9220067fd8b187 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 30 Dec 2017 22:22:17 +0900 Subject: [PATCH 03/80] Union Find with Undo --- data_structure/undoable_union_find.cc | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 data_structure/undoable_union_find.cc diff --git a/data_structure/undoable_union_find.cc b/data_structure/undoable_union_find.cc new file mode 100644 index 0000000..a2b0b3c --- /dev/null +++ b/data_structure/undoable_union_find.cc @@ -0,0 +1,120 @@ +// +// Undoable Union Find +// +// It can undo each unite operation. +// +// Naming: +// Data structures with undo operation is weaker than +// partially-persistence (accessible all older vers) and +// slightly stronger than semi-persistence (backtrackable). +// +#include +#include +#include +#include +#include +#include + +using namespace std; +#define fst first +#define snd second +#define all(c) begin(c), end(c) + +struct undoable_union_find { + vector p; + undoable_union_find(int n) : p(n, -1) { }; + vector> hist; + bool unite(int u, int v) { + if ((u = root(u)) == (v = root(v))) return false; + if (p[u] > p[v]) swap(u, v); + hist.push_back(make_tuple(u, v, p[v])); + p[u] += p[v]; p[v] = u; + return true; + } + void undo() { + int u, v, w; tie(u, v, w) = hist.back(); + hist.pop_back(); + p[v] = w; + p[u] -= p[v]; + } + bool find(int u, int v) { return root(u) == root(v); } + int root(int u) { for (; p[u] >= 0; u = p[u]); return u; } + int size(int u) { return -p[root(u)]; } +}; + +struct offline_dynamic_connectivity { + int n; + undoable_union_find uf; + offline_dynamic_connectivity(int n) : n(n), uf(n) { } + + typedef pair edge; + vector query; + vector ans; + map>> appear; + + void add_edge(int u, int v) { + if (u > v) swap(u, v); + appear[{u,v}].push_back({query.size(), 1<<30}); + } + void erase_edge(int u, int v) { + if (u > v) swap(u, v); + appear[{u,v}].back().snd = query.size(); + } + int is_connected(int u, int v) { + query.push_back({u,v}); + return query.size()-1; + } + + vector> es; + void insert(int l, int r, int k, int s, int t, edge e) { + s = max(s, l); t = min(r, t); + if (s >= t) return; + if (s == l && t == r) { + es[k].insert(e); + } else { + insert(l, (l+r)/2, 2*k+1, s, t, e); + insert((l+r)/2, r, 2*k+2, s, t, e); + if (es[2*k+1].count(e) && es[2*k+2].count(e)) { + es[2*k+1].erase(e); + es[2*k+2].erase(e); + es[k].insert(e); + } + } + } + void rec(int l, int r, int k) { + if (l >= r) return; + for (edge e: es[k]) uf.unite(e.fst, e.snd); + if (l+1 == r) { + ans[l] = uf.find(query[l].fst, query[l].snd); + } else { + rec(l, (l+r)/2, 2*k+1); + rec((l+r)/2, r, 2*k+2); + } + for (edge e: es[k]) uf.undo(); + } + void solve() { + int q = query.size(); + es.resize(4*q); + for (auto a: appear) + for (auto b: a.snd) + insert(0, q, 0, b.fst, b.snd, a.fst); + ans.assign(q, 0); + rec(0, q, 0); + } +}; + +int main() { + offline_dynamic_connectivity solver(3); + solver.is_connected(0,1); + solver.add_edge(0,1); + solver.is_connected(0,1); + solver.is_connected(1,2); + solver.add_edge(1,2); + solver.is_connected(0,2); + solver.erase_edge(0,1); + solver.is_connected(0,2); + solver.solve(); + for (int i = 0; i < solver.query.size(); ++i) { + cout << solver.query[i].fst << " " << solver.query[i].snd << " " << solver.ans[i] << endl; + } +} From bca7619d9c91f6b0e92c6f44c63a69d7146e187f Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Mon, 1 Jan 2018 01:41:43 +0900 Subject: [PATCH 04/80] Link-Cut Tree (simple) --- graph/link_cut_tree.cc | 148 ++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 76 deletions(-) diff --git a/graph/link_cut_tree.cc b/graph/link_cut_tree.cc index b1217eb..7b7bd6c 100644 --- a/graph/link_cut_tree.cc +++ b/graph/link_cut_tree.cc @@ -1,8 +1,11 @@ // -// Link Cut Tree +// Link Cut Tree (Slator-Tarjan) // // Decription: -// It maintains rooted forests with link/cut operations +// It maintains rooted arborescences with the following operations +// link(u,v) : add link from u to v +// cut(u) : cut link from u (to the root direction) +// lca(u,v) : least common ancestor of u and v // // Algorithm: // Classify links into solid and dashed. @@ -18,103 +21,96 @@ // D. D. Sleator and R. E. Tarjan (1983): // A Data Structure for Dynamic Trees. // Journal oF Computer and System Sciences, vol. 26, no. 3, pp. 362-391. +// +// Verified: AOJ Spaceships + #include #include #include +#include using namespace std; struct link_cut_tree { struct node { - int x, s; // value and sum - node *ch[2], *p; + node *child[2], *parent; }; - int sum(node *t) { return t ? t->s : 0; } - node *update(node *t) { - if (t) t->s = t->x + sum(t->ch[0]) + sum(t->ch[1]); - return t; - } - node *make_node(int x) { return new node({x, x, 0, 0, 0}); } - - int dir(node *t) { return t != t->p->ch[0]; } - bool is_root(node *t) { - return !t->p || (t->p->ch[0] != t && t->p->ch[1] != t); - } - void connect(node *p, node *t, int d) { - p->ch[d] = t; if (t) t->p = p; - update(p); + bool is_root(node *x) { + return !x->parent || (x->parent->child[0] != x + && x->parent->child[1] != x); } - void rot(node *t) { - node *p = t->p; - int d = dir(t); - if (!is_root(p)) connect(p->p, t, dir(p)); - else t->p = p->p; - connect(p, t->ch[!d], d); - connect(t, p, !d); + int dir(node *x) { return x->parent && x->parent->child[1] == x; } + void rot(node* t) { + node *p = t->parent, *g = p->parent; + int d = dir(t); + p->child[d] = t->child[!d]; + if (p->child[d]) p->child[d]->parent = p; + if (!is_root(p)) g->child[dir(p)] = t; + t->parent = g; + t->child[!d] = p; + p->parent = t; } - void splay(node *t) { - for (; !is_root(t); rot(t)) - if (!is_root(t->p)) rot(dir(t) == dir(t->p) ? t->p : t); + void splay(node *x) { + while (!is_root(x)) { + if (!is_root(x->parent)) { + if (dir(x) == dir(x->parent)) rot(x->parent); + else rot(x); + } + rot(x); + } } - node *expose(node *t) { - node *l = 0; - for (node *s = t; s; s = s->p) { - splay(s); - connect(s, l, 1); - l = s; + node *expose(node *x) { + node *r = 0; + for (node *p = x; p; p = p->parent) { + splay(p); + p->child[1] = r; + r = p; } - splay(t); - return l; + splay(x); + return r; } - void link(node *t, node *p) { - expose(t); - expose(p); - t->p = p; + + vector ns; + link_cut_tree(int n) : ns(n) { + for (int i = 0; i < n; ++i) + ns[i].child[0] = ns[i].child[1] = ns[i].parent = 0; } - void cut(node *t) { - expose(t); - t->ch[0] = t->ch[0]->p = 0; + void link(int x, int y) { + expose(&ns[x]); + expose(&ns[y]); + ns[y].child[1] = &ns[x]; + ns[x].parent = &ns[y]; } - node *lca(node *s, node *t) { - expose(s); - node *u = expose(t); - return !s->p ? 0 : u; + void cut(int x) { + expose(&ns[x]); + node *y = ns[x].child[0]; + ns[x].child[0] = y->parent = 0; } - int sum_to_root(node *t) { - expose(t); - return sum(t->ch[0]) + t->x; + int lca(int x, int y) { + expose(&ns[x]); + node *u = expose(&ns[y]); + return ns[x].parent ? u - &ns[0] : -1; } }; int main() { - link_cut_tree LCT; - int n, m; cin >> n >> m; - - vector a(n); - for (int i = 0; i < n; ++i) { - a[i] = LCT.make_node(i+1); - } + int n, q, t, a, b; + scanf("%d %d", &n, &q); - int u, v; - link_cut_tree::node *p; - for (int k = 0; k < m; ++k) { - int t; cin >> t; - switch (t) { - case 1: - cin >> u >> v; --u; --v; - LCT.link(a[u], a[v]); - break; - case 2: - cin >> u; --u; - LCT.cut(a[u]); - break; - case 3: - cin >> u >> v; --u; --v; - p = LCT.lca(a[u], a[v]); - if (!p) cout << "-1" << endl; - else cout << p->x << endl; - break; + link_cut_tree LCT(n); + for (int i = 0; i < q; ++i) { + scanf("%d", &t); + if (t == 1) { + scanf("%d %d", &a, &b); + LCT.link(a-1, b-1); + } else if (t == 2) { + scanf("%d", &a); + LCT.cut(a-1); + } else { + scanf("%d %d", &a, &b); + int c = LCT.lca(a-1, b-1); + printf("%d\n", c < 0 ? c : c+1); } } } From 68bf05a2ad3edd97267b1456600727ea6044fc18 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Mon, 1 Jan 2018 01:44:05 +0900 Subject: [PATCH 05/80] Link Cut Tree (simple) --- graph/link_cut_tree.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graph/link_cut_tree.cc b/graph/link_cut_tree.cc index 7b7bd6c..0e8fcb6 100644 --- a/graph/link_cut_tree.cc +++ b/graph/link_cut_tree.cc @@ -42,12 +42,12 @@ struct link_cut_tree { } int dir(node *x) { return x->parent && x->parent->child[1] == x; } void rot(node* t) { - node *p = t->parent, *g = p->parent; + node *p = t->parent; int d = dir(t); p->child[d] = t->child[!d]; if (p->child[d]) p->child[d]->parent = p; - if (!is_root(p)) g->child[dir(p)] = t; - t->parent = g; + if (!is_root(p)) p->parent->child[dir(p)] = t; + t->parent = p->parent; t->child[!d] = p; p->parent = t; } From e79e5af1490e5ff5e96ca8bfa5c81239a51c0aa6 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 2 Jan 2018 23:42:03 +0900 Subject: [PATCH 06/80] Union Find data structure --- data_structure/union_find.cc | 151 ++++++++++++++++++++++++++++++++--- 1 file changed, 142 insertions(+), 9 deletions(-) diff --git a/data_structure/union_find.cc b/data_structure/union_find.cc index 2cc999d..7e89276 100644 --- a/data_structure/union_find.cc +++ b/data_structure/union_find.cc @@ -1,17 +1,150 @@ +// +// Union Find Data Structure +// +// Description: +// An union-find data structure (aka. disjoint set data structure) +// maintains a disjoint sets and supports the following operations. +// - unite(u, v): merge sets containing u and v. +// - find(u, v) : return true if u and v are in the same set +// - size(u) : size of the set containing u. // -// Union Find data structure +// The weighted version additionally maintains the values for the +// elements and supports the following operations: +// - add(u, a) : value[u] += a +// - addSet(u, a): value[v] += a for v in set(u) +// - get(u) : return value[u] +// - getSet(u) : return sum(value[v] for v in set(u)) // +// Complexity: +// Amortized O(a(n)) for all operations. +// Here, a(n) is the inverse Ackermann function, which is +// less than five for a realistic size input. +// +// Verified: +// AOJ1330 http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1330 +// and other many problems. +// + +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) -struct union_find { - vector p; - union_find(int n) : p(n, -1) { }; +struct UnionFind { + vector parent; // parent[root] is the negative of the size. + UnionFind(int n) : parent(n, -1) { }; bool unite(int u, int v) { - if ((u = root(u)) == (v = root(v))) return false; - if (p[u] > p[v]) swap(u, v); - p[u] += p[v]; p[v] = u; + u = root(u); v = root(v); + if (u == v) return false; + if (parent[u] > parent[v]) swap(u, v); + parent[u] += parent[v]; parent[v] += u; return true; } bool find(int u, int v) { return root(u) == root(v); } - int root(int u) { return p[u] < 0 ? u : p[u] = root(p[u]); } - int size(int u) { return -p[root(u)]; } + int root(int u) { return parent[u] < 0 ? u : parent[u] = root(parent[u]); } + int size(int u) { return -parent[root(u)]; } }; + +template +struct WeightedUnionFind { + struct Data { + int parent = -1; + T value = 0, delta = 0, total = 0; + }; + vector data; + WeightedUnionFind(int n) : data(n) { } + + void add(int u, T a) { + data[u].value += a; + data[root(u)].total += a; + } + void addSet(int u, T a) { + data[root(u)].delta += a; + data[root(u)].total -= data[root(u)].parent * a; + } + T get(int u) { + return data[u].value + root_(u).snd; + } + T getSet(int u) { + return data[root(u)].total; + } + bool unite(int u, int v) { + u = root(u); v = root(v); + if (u == v) return false; + if (data[u].parent > data[v].parent) swap(u, v); + data[u].parent += data[v].parent; + data[v].parent = u; + data[v].delta -= data[u].delta; + data[u].total += data[v].total; + return true; + } + pair root_(int u) { + if (data[u].parent < 0) return {u, data[u].delta}; + auto p = root_(data[u].parent); + p.snd += data[u].delta; + data[u].parent = p.fst; + data[u].delta = p.snd - data[p.fst].delta; + return p; + } + int root(int u) { return root_(u).fst; } + bool find(int u, int v) { + return root(u) == root(v); + } + int size(int u) { + return -data[root(u)].parent; + } +}; + +int test() { + int n = 5; + WeightedUnionFind uf(n); + for (int i = 0; i < n; ++i) { + uf.add(i, i); + } + uf.unite(2, 4); + for (int i = 0; i < n; ++i) { + cout << uf.get(i) << " " << uf.getSet(i) << endl; + } + cout << endl; + uf.unite(3, 4); + for (int i = 0; i < n; ++i) { + cout << uf.get(i) << " " << uf.getSet(i) << endl; + } + cout << endl; + uf.add(3,10); + for (int i = 0; i < n; ++i) { + cout << uf.get(i) << " " << uf.getSet(i) << endl; + } + cout << endl; + uf.addSet(4,5); + for (int i = 0; i < n; ++i) { + cout << uf.get(i) << " " << uf.getSet(i) << endl; + } +} + +void AOJ1330() { + for (int n, m; cin >> n >> m && n; ) { + WeightedUnionFind uf(n); + for (int i = 0; i < m; ++i) { + char c[2]; + int a, b; + scanf("%s %d %d", c, &a, &b); + --a; --b; + if (c[0] == '!') { + long long x; + scanf("%Ld", &x); + uf.addSet(b, uf.get(a)-uf.get(b)-x); + uf.unite(a, b); + } else { + if (uf.find(a, b)) { + printf("%Ld\n", uf.get(a)-uf.get(b)); + } else { + printf("UNKNOWN\n"); + } + } + } + } +} +int main() { AOJ1330(); } From 6c4a86dea5a487dbdd5fe96b3589d950e42c370d Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 2 Jan 2018 23:51:38 +0900 Subject: [PATCH 07/80] Union Find --- data_structure/union_find.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structure/union_find.cc b/data_structure/union_find.cc index 7e89276..6a56b5e 100644 --- a/data_structure/union_find.cc +++ b/data_structure/union_find.cc @@ -39,7 +39,7 @@ struct UnionFind { u = root(u); v = root(v); if (u == v) return false; if (parent[u] > parent[v]) swap(u, v); - parent[u] += parent[v]; parent[v] += u; + parent[u] += parent[v]; parent[v] = u; return true; } bool find(int u, int v) { return root(u) == root(v); } From bd586ff14ffb1065fac4ccf00a5989eb76d9edd2 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 2 Jan 2018 23:53:36 +0900 Subject: [PATCH 08/80] Union Find with Undo --- ...able_union_find.cc => !union_find_undo.cc} | 89 ++++++++++--------- 1 file changed, 46 insertions(+), 43 deletions(-) rename data_structure/{undoable_union_find.cc => !union_find_undo.cc} (51%) diff --git a/data_structure/undoable_union_find.cc b/data_structure/!union_find_undo.cc similarity index 51% rename from data_structure/undoable_union_find.cc rename to data_structure/!union_find_undo.cc index a2b0b3c..4aff857 100644 --- a/data_structure/undoable_union_find.cc +++ b/data_structure/!union_find_undo.cc @@ -20,81 +20,84 @@ using namespace std; #define snd second #define all(c) begin(c), end(c) -struct undoable_union_find { - vector p; - undoable_union_find(int n) : p(n, -1) { }; - vector> hist; + +struct UndoableUnionFind { + vector parent; + vector> history; + UndoableUnionFind(int n) : parent(n, -1) { }; bool unite(int u, int v) { - if ((u = root(u)) == (v = root(v))) return false; - if (p[u] > p[v]) swap(u, v); - hist.push_back(make_tuple(u, v, p[v])); - p[u] += p[v]; p[v] = u; + u = root(u); v = root(v); + if (u == v) return false; + if (parent[u] > parent[v]) swap(u, v); + history.push_back(make_tuple(u, v, parent[v])); + parent[u] += parent[v]; parent[v] = u; return true; } void undo() { - int u, v, w; tie(u, v, w) = hist.back(); - hist.pop_back(); - p[v] = w; - p[u] -= p[v]; + int u, v, w; + tie(u, v, w) = history.back(); + history.pop_back(); + parent[v] = w; + parent[u] -= parent[v]; } bool find(int u, int v) { return root(u) == root(v); } - int root(int u) { for (; p[u] >= 0; u = p[u]); return u; } - int size(int u) { return -p[root(u)]; } + int root(int u) { return parent[u] < 0 ? u : parent[u] = root(parent[u]); } + int size(int u) { return -parent[root(u)]; } }; -struct offline_dynamic_connectivity { + +struct OfflineDynamicConnectivity { int n; - undoable_union_find uf; - offline_dynamic_connectivity(int n) : n(n), uf(n) { } + UndoableUnionFind uf; + OfflineDynamicConnectivity(int n) : n(n), uf(n) { } - typedef pair edge; - vector query; + typedef pair Edge; + vector query; vector ans; - map>> appear; + map>> appear; - void add_edge(int u, int v) { + void addEdge(int u, int v) { if (u > v) swap(u, v); appear[{u,v}].push_back({query.size(), 1<<30}); } - void erase_edge(int u, int v) { + void eraseEdge(int u, int v) { if (u > v) swap(u, v); appear[{u,v}].back().snd = query.size(); } - int is_connected(int u, int v) { + int isConnected(int u, int v) { query.push_back({u,v}); return query.size()-1; } - - vector> es; - void insert(int l, int r, int k, int s, int t, edge e) { + vector> edges; + void insert(int l, int r, int k, int s, int t, Edge e) { s = max(s, l); t = min(r, t); if (s >= t) return; if (s == l && t == r) { - es[k].insert(e); + edges[k].insert(e); } else { insert(l, (l+r)/2, 2*k+1, s, t, e); insert((l+r)/2, r, 2*k+2, s, t, e); - if (es[2*k+1].count(e) && es[2*k+2].count(e)) { - es[2*k+1].erase(e); - es[2*k+2].erase(e); - es[k].insert(e); + if (edges[2*k+1].count(e) && edges[2*k+2].count(e)) { + edges[2*k+1].erase(e); + edges[2*k+2].erase(e); + edges[k].insert(e); } } } void rec(int l, int r, int k) { if (l >= r) return; - for (edge e: es[k]) uf.unite(e.fst, e.snd); + for (Edge e: edges[k]) uf.unite(e.fst, e.snd); if (l+1 == r) { ans[l] = uf.find(query[l].fst, query[l].snd); } else { rec(l, (l+r)/2, 2*k+1); rec((l+r)/2, r, 2*k+2); } - for (edge e: es[k]) uf.undo(); + for (Edge e: edges[k]) uf.undo(); } void solve() { int q = query.size(); - es.resize(4*q); + edges.resize(4*q); for (auto a: appear) for (auto b: a.snd) insert(0, q, 0, b.fst, b.snd, a.fst); @@ -104,15 +107,15 @@ struct offline_dynamic_connectivity { }; int main() { - offline_dynamic_connectivity solver(3); - solver.is_connected(0,1); - solver.add_edge(0,1); - solver.is_connected(0,1); - solver.is_connected(1,2); - solver.add_edge(1,2); - solver.is_connected(0,2); - solver.erase_edge(0,1); - solver.is_connected(0,2); + OfflineDynamicConnectivity solver(3); + solver.isConnected(0,1); + solver.addEdge(0,1); + solver.isConnected(0,1); + solver.isConnected(1,2); + solver.addEdge(1,2); + solver.isConnected(0,2); + solver.eraseEdge(0,1); + solver.isConnected(0,2); solver.solve(); for (int i = 0; i < solver.query.size(); ++i) { cout << solver.query[i].fst << " " << solver.query[i].snd << " " << solver.ans[i] << endl; From f704c1cd254859087265d99c38f870ae7feaabe6 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Wed, 3 Jan 2018 00:09:24 +0900 Subject: [PATCH 09/80] Cartesian Tree --- data_structure/cartesian_tree.cc | 106 +++++++++++++++---------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/data_structure/cartesian_tree.cc b/data_structure/cartesian_tree.cc index 45a40c8..7efa2b2 100644 --- a/data_structure/cartesian_tree.cc +++ b/data_structure/cartesian_tree.cc @@ -2,35 +2,28 @@ // Cartesian Tree // // Description: -// For a given sequence x, the Cartesian tree is recursively +// For a given sequence xs, the Cartesian tree is recursively // defined as follows: -// - root is the minimum in x. -// - the left child is the Cartesian tree for the the left of the min., -// and the right child is the Cartesian tree for the right of the min. -// -// Algorithm: -// Left-to-right construction. -// -// Applications: -// In order traversal gives the original sequence. -// Sorting can be performed in O(n log k) -// LCA of two element gives RMQ in original sequence. -// -// Complexity: -// construction: O(n) -// sort: O(n log k) by using priority queue. -// offline RMQ: O(n + q) by Tarjan's offline LCA. +// - the root is the minimum in xs. +// - the left child is the Cartesian tree for the the left segment, +// and the right child is the Cartesian tree for the right segment. +// It is constructed in O(n) time by the left-to-right traversal. // +// By using the Cartesian tree, we can solve +// - Sorting in O(n log k) time, where k is the number of consecutive +// sorted subsegments. +// - LCA(i,j) = RMQ(i,j). Thus, by using the Tarjan's Offline LCA, +// we can solve m RMQs in O(m a(n)) time. +// // Verified: -// SPOJ11772, SPOJ1005604 for RMQ. -// +// SPOJ RPLN: http://www.spoj.com/problems/RPLN/ +// // References: // C. Levcopoulos and O. Petersson (1989): // Heapsort - Adapted for Presorted Files. // in Proceedings of the Workshop on Algorithms and Data Structures, // pp. 499-509. - #include #include #include @@ -45,76 +38,83 @@ using namespace std; #define all(c) ((c).begin()), ((c).end()) template -struct cartesian_tree { +struct CartesianTree { int n, root; - vector x; - vector l, r, p; - cartesian_tree(const vector &x) - : n(x.size()), x(x), l(n,-1), r(n,-1), p(n,-1) { + vector xs; + struct Node { + int left = -1, right = -1, parent = -1; + }; + vector node; + CartesianTree(const vector &xs) : n(xs.size()), xs(xs), node(n) { root = 0; for (int i = 1; i < n; ++i) { int j = i-1; - while (p[j] >=0 && x[i] < x[j]) j = p[j]; - if (x[i] < x[j]) { - p[j] = i; - l[i] = j; + while (node[j].parent >=0 && xs[i] < xs[j]) + j = node[j].parent; + if (xs[i] < xs[j]) { + node[j].parent = i; + node[i].left = j; root = i; } else { - if (r[j] >= 0) p[r[j]] = i; - l[i] = r[j]; - p[i] = j; - r[j] = i; + if (node[j].right >= 0) node[node[j].right].parent = i; + node[i].left = node[j].right; + node[i].parent = j; + node[j].right = i; } } } // In-order traverse gives an original sequence void traverse(int t, int tab = 0) { if (t < 0) return; - traverse(l[t], tab + 2); + traverse(node[t].left, tab + 2); for (int i = 0; i < tab; ++i) cout << " "; - cout << x[t] << endl; - traverse(r[t], tab + 2); + cout << xs[t] << endl; + traverse(node[t].right, tab + 2); } void traverse() { traverse(root); } // Sorting in O(n log k) by Levcopoulos-Petersson void sort() { - auto comp = [&](int i, int j) { return x[i] > x[j]; }; + auto comp = [&](int i, int j) { return xs[i] > xs[j]; }; priority_queue, decltype(comp)> que(comp); que.push(root); while (!que.empty()) { int t = que.top(); que.pop(); - cout << x[t] << " "; - if (l[t] >= 0) que.push(l[t]); - if (r[t] >= 0) que.push(r[t]); + cout << xs[t] << " "; + if (node[t].left >= 0) que.push(node[t].left); + if (node[t].right >= 0) que.push(node[t].right); } cout << endl; } - struct union_find { - vector p; - union_find(int n) : p(n, -1) { }; + struct UnionFind { + vector parent; // parent[root] is the negative of the size. + UnionFind(int n) : parent(n, -1) { }; bool unite(int u, int v) { - if ((u = root(u)) == (v = root(v))) return false; - if (p[u] > p[v]) swap(u, v); - p[u] += p[v]; p[v] = u; + u = root(u); v = root(v); + if (u == v) return false; + if (parent[u] > parent[v]) swap(u, v); + parent[u] += parent[v]; parent[v] = u; return true; } - int root(int u) { return p[u] < 0 ? u : p[u] = root(p[u]); } + bool find(int u, int v) { return root(u) == root(v); } + int root(int u) { return parent[u] < 0 ? u : parent[u] = root(parent[u]); } + int size(int u) { return -parent[root(u)]; } }; + struct query { int u, v, a; }; - void range_min_queries(vector &queries) { + void rangeMinQueries(vector &queries) { vector> Q(n); for (auto &q: queries) { Q[q.u].push_back(&q); Q[q.v].push_back(&q); } - union_find uf(n); + UnionFind uf(n); vector anc(n), color(n); iota(all(anc), 0); function rec = [&](int u) { - for (int c: {l[u], r[u]}) { + for (int c: {node[u].left, node[u].right}) { if (c < 0) continue; rec(c); uf.unite(c, u); @@ -140,14 +140,14 @@ int main() { vector x(n); for (int i = 0; i < n; ++i) scanf("%d", &x[i]); - cartesian_tree T(x); + CartesianTree T(x); - vector::query> qs(q); + vector::query> qs(q); for (int i = 0; i < q; ++i) { scanf("%d %d", &qs[i].u, &qs[i].v); --qs[i].u; --qs[i].v; } - T.range_min_queries(qs); + T.rangeMinQueries(qs); for (auto q: qs) printf("%d\n", x[q.a]); } From 78ff9912b30bc6bdc1b52e576c55f77260add11b Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Wed, 3 Jan 2018 16:12:08 +0900 Subject: [PATCH 10/80] Partially Persistent Union Find --- .../partially_persistent_union_find.cc | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 data_structure/partially_persistent_union_find.cc diff --git a/data_structure/partially_persistent_union_find.cc b/data_structure/partially_persistent_union_find.cc new file mode 100644 index 0000000..1375395 --- /dev/null +++ b/data_structure/partially_persistent_union_find.cc @@ -0,0 +1,77 @@ +// +// Partially Persistent Union Find +// +// Description: +// It is a persistent version of union find data structure. +// It allows us to apply "find" to the all versions and +// "unite" to the latest version. +// +// Complexity: +// O(log n) for each query. +// +// Verified: +// CODE THANKS FESTIVAL 2017, Union Set: +// https://code-thanks-festival-2017-open.contest.atcoder.jp/tasks/code_thanks_festival_2017_h +// + +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +using namespace std; + +struct PartiallyPersistentUnionFind { + vector>> parent; // (parent index, modified time) + int now = 0; // time = 0 is the initial state + PartiallyPersistentUnionFind(int n) : parent(n, {{-1,0}}) { } + bool unite(int u, int v) { + ++now; + u = root(u, now); v = root(v, now); + if (u == v) return false; + if (parent[u].back().fst > parent[v].back().fst) swap(u, v); + parent[u].push_back({parent[u].back().fst+parent[v].back().fst, now}); + parent[v].push_back({u, now}); + return true; + } + bool find(int u, int v, int t) { return root(u, t) == root(v, t); } + int root(int u, int t) { + if (parent[u].back().fst >= 0 && parent[u].back().snd <= t) + return root(parent[u].back().fst, t); + return u; + } + int size(int u, int t) { + u = root(u, t); + int lo = 0, hi = parent[u].size(); + while (lo + 1 < hi) { + int mi = (lo + hi) / 2; + if (parent[u][mi].snd <= t) lo = mi; + else hi = mi; + } + return -parent[u][lo].fst; + } +}; + +int main() { + int n, m, q, a, b; + scanf("%d %d", &n, &m); + + PartiallyPersistentUnionFind uf(n); + for (int i = 0; i < m; ++i) { + scanf("%d %d", &a, &b); --a; --b; + uf.unite(a, b); + } + scanf("%d", &q); + for (int i = 0; i < q; ++i) { + scanf("%d %d", &a, &b); --a; --b; + int lo = 0, hi = uf.now+1; + while (lo+1 < hi) { + int mi = (lo + hi) / 2; + if (uf.find(a, b, mi)) hi = mi; + else lo = mi; + } + if (hi == uf.now+1) printf("-1\n"); + else printf("%d\n", hi); + } +} From 3c0dc8722b025f2db8d44eb70ff156a682670eeb Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Wed, 3 Jan 2018 16:12:37 +0900 Subject: [PATCH 11/80] Union Find with Undo --- data_structure/{!union_find_undo.cc => union_find_undo.cc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data_structure/{!union_find_undo.cc => union_find_undo.cc} (100%) diff --git a/data_structure/!union_find_undo.cc b/data_structure/union_find_undo.cc similarity index 100% rename from data_structure/!union_find_undo.cc rename to data_structure/union_find_undo.cc From 628b02807e0fdaa011f7144286311138474a22c9 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Wed, 3 Jan 2018 18:54:18 +0900 Subject: [PATCH 12/80] Retroactive Queue --- data_structure/retroactive_queue.cc | 169 ++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 data_structure/retroactive_queue.cc diff --git a/data_structure/retroactive_queue.cc b/data_structure/retroactive_queue.cc new file mode 100644 index 0000000..ca63da9 --- /dev/null +++ b/data_structure/retroactive_queue.cc @@ -0,0 +1,169 @@ +// +// Fully Retroactive Queue +// +// Description: +// It maintains a list of actions ("push" and "pop"). +// We can insert/erase the actions and ask status ("front") in +// any position of the list. +// +// To implement this structure, we maintain the list by a BST +// and keep track the number of pushs/pops in each position +// by using the range addition and range minimum. +// +// Complexity: +// O(n log n). +// +// References: +// E. Demaine, J. Iacono, and S. Langerman (2007): +// "Retroactive data structures". ACM Transactions of Algorithms, +// vol.3, no.2, pp.1--21. +// + +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +template +struct RetroactiveQueue { + struct Node { // Splay Tree + int type; // 0: none, 1: push, 2: pop + T value; + Node *child[2], *parent; + int a_push, d_push, a_pop, d_pop, min_remain; + } *head; + int remain(Node *x) { + return !x ? 0 : x->a_push + x->d_push - x->a_pop - x->d_pop; + } + RetroactiveQueue() : head(new Node({0})) { } + Node *update(Node *x) { + if (!x) return x; + x->min_remain = remain(x); + for (int i: {0, 1}) + if (x->child[i]) + x->min_remain = min(x->min_remain, x->child[i]->min_remain); + return x; + } + Node *pushdown(Node *x) { + if (!x) return x; + for (int i: {0, 1}) { + if (x->child[i]) { + x->child[i]->d_push += x->d_push; + x->child[i]->d_pop += x->d_pop; + } + } + x->a_push += x->d_push; + x->a_pop += x->d_pop; + x->d_push = x->d_pop = 0; + return x; + } + int dir(Node *x) { return x->parent && x->parent->child[1] == x; } + void link(Node *x, Node *y, int d) { + if (x) x->child[d] = y; + if (y) y->parent = x; + } + void rot(Node *x) { + int d = dir(x); + Node *p = x->parent; + pushdown(p->parent); pushdown(p); pushdown(x); + link(p->parent, x, dir(p)); + link(p, x->child[!d], d); + link(x, p, !d); + update(p); update(x); + } + void splay(Node *x) { + if (!x) return; + while (x->parent) { + if (x->parent->parent) { + if (dir(x) == dir(x->parent)) rot(x->parent); + else rot(x); + } + rot(x); + } + pushdown(x); + } + Node *insert(Node *x, Node *y) { + splay(x); + y->child[0] = x; + x->parent = y; + y->child[1] = x->child[1]; + x->child[1] = 0; + if (y->child[1]) { + y->child[1]->parent = y; + if (y->type == 1) y->child[1]->d_push += 1; + else if (y->type == 2) y->child[1]->d_pop += 1; + } + y->a_push = x->a_push + x->d_push + (y->type & 1); + y->a_pop = x->a_pop + x->d_pop + (y->type >> 1); + update(y->child[0]); + update(y->child[1]); + update(y); + return y; + } + Node *insert_push(Node *x, T a) { return insert(x, new Node({1, a})); } + Node *insert_pop(Node *x) { return insert(x, new Node({2})); } + Node *erase(Node *x) { + splay(x); + Node *y = x->child[1]; + if (!y) { + x = x->child[0]; + x->parent = 0; + return x; + } + if (x->type == 1) y->d_push -= 1; + else if (x->type == 2) y->d_pop -= 1; + y->parent = 0; + update(y); + while (y->child[0]) y = y->child[0]; + splay(y); + y->child[0] = x->child[0]; + if (y->child[0]) y->child[0]->parent = y; + update(y->child[0]); + update(y); + return y; + } + bool valid(Node *x) { splay(x); return x->min_remain >= 0; } + T front(Node *x) { + splay(x); + int k = x->a_pop + x->d_pop; + for (Node *y = x; y; ) { + pushdown(y); + if (y->a_push > k) { + x = y; + y = y->child[0]; + } else { + y = y->child[1]; + } + } + return x->value; + } + + + void display() { + splay(head); + display(head, 0); + } + void display(Node *x, int tab = 0) { + if (!x) return; + display(x->child[0], tab+2); + cout << string(tab, ' ') << x->type << " " << x->value << " " << x->a_push << " " << x->d_push << " " << x->a_pop << " " << x->d_pop << " " << x->min_remain << endl; + display(x->child[1], tab+2); + } +}; + +int main() { + RetroactiveQueue que; + auto i1 = que.insert_push(que.head, 10); + auto i2 = que.insert_push(i1, 20); + auto i3 = que.insert_push(i2, 30); + auto i4 = que.insert_pop(i3); + //auto i5 = que.insert_pop(i2); + que.erase(i1); + que.display(); + cout << que.front(i1) << endl; + cout << que.front(i2) << endl; + cout << que.front(i3) << endl; + cout << que.front(i4) << endl; +} From 53a285936e7419c0848903a607036f679d6cde90 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Wed, 3 Jan 2018 20:18:44 +0900 Subject: [PATCH 13/80] Initializable Array --- data_structure/initializable_array.cc | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 data_structure/initializable_array.cc diff --git a/data_structure/initializable_array.cc b/data_structure/initializable_array.cc new file mode 100644 index 0000000..37c45a3 --- /dev/null +++ b/data_structure/initializable_array.cc @@ -0,0 +1,66 @@ +// +// Initializable Array +// +// Description: +// It allows the following operations in O(1) time. +// - init(a): initialize xs[i] = a for all i +// - xs[i]: return xs[i] +// - set(i, a): set xs[i] = a +// The important operation is "init", which usually +// requires O(n) time. By maintaining timestamps, +// we can "emulate" the initialization. +// +// Complexity: +// O(1). +// +// References: +// J. Bentley (1986): Programming pearls. Addison-Wesley. +// +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +template +struct InitializableArray { + T initv, *value; + size_t b, *from, *to; + InitializableArray(int n) { + value = new T[n]; + from = new size_t[n]; + to = new size_t[n]; + } + bool chain(int i) { + int j = from[i]; + return j < b && to[j] == i; + } + void init(T a) { + initv = a; + b = 0; + } + T operator[](int i) { + return chain(i) ? value[i] : initv; + } + void set(int i, T a) { + if (!chain(i)) { + from[i] = b; + to[b++] = i; + } + value[i] = a; + } +}; + +int main() { + InitializableArray a(3); + cout << a.value[0] << endl; + a.init(0); + a.init(2); + a.set(1, 5); + cout << a[0] << endl; + cout << a[1] << endl; + cout << a[2] << endl; + a.set(2, 3); + a.init(0); +} From 69a6a4514b348ae356b9bd0e9285f5442150314c Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Fri, 5 Jan 2018 23:27:10 +0900 Subject: [PATCH 14/80] Continued Fraction --- math/continued_fraction.cc | 176 +++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 math/continued_fraction.cc diff --git a/math/continued_fraction.cc b/math/continued_fraction.cc new file mode 100644 index 0000000..2a34d84 --- /dev/null +++ b/math/continued_fraction.cc @@ -0,0 +1,176 @@ +// +// Continued Fraction +// +// Description: +// see https://perl.plover.com/yak/cftalk/ +// +// Gosper's algorithm +// +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +using Int = long long; +struct Generator { + using Pointer = shared_ptr; + virtual Int value() = 0; + virtual Pointer next() = 0; +}; + +// (a x + b)/(c x + d) +struct HoloGen : Generator { + Int a, b, c, d; + Pointer x; + HoloGen(Pointer x, Int a, Int b, Int c, Int d) : x(x), a(a), b(b), c(c), d(d) { } + void normalize() { + while (1) { + if (c == 0 && d == 0) break; + double b_ac = (double)a/c, b_bd = (double)b/d; + if ((Int)b_ac == (Int)b_bd) break; + if (!x) { + tie(a,b,c,d) = make_tuple(a,a,c,c); + } else { + Int p = x->value(); x = x->next(); + tie(a,b,c,d) = make_tuple(a*p+b,a,c*p+d,c); + } + } + } + virtual Int value() { normalize(); return a/c; } + virtual Pointer next() { + Int r = value(); + if (a-c*r == 0 && b-d*r == 0) return 0; + return Pointer(new HoloGen(x,c,d,a-c*r,b-d*r)); + } +}; +// (a x y + b x + c y + d)/(e x y + f x + g y + h) +struct ArithGen : Generator { + Int a, b, c, d, e, f, g, h; + Pointer x, y; + ArithGen(Pointer x, Pointer y, Int a, Int b, Int c, Int d, Int e, Int f, Int g, Int h) : x(x), y(y), a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h) { } + void normalize() { + while (1) { + if (e == 0 && f == 0 && g == 0 && h == 0) break; + double b_ae = (double)a/e, b_cg = (double)c/g, + b_bf = (double)b/f, b_dh = (double)d/h; + if ((Int)b_ae == (Int)b_cg && + (Int)b_cg == (Int)b_bf && + (Int)b_bf == (Int)b_dh) break; + auto idiff = [&](double a, double b) { + if (isinf(a) && isinf(b)) return 0.0; + if (isinf(a) || isinf(b)) return 1.0/0.0; + return fabs(a - b); + }; + if (max(idiff(b_ae, b_cg), idiff(b_bf, b_dh)) + > max(idiff(b_ae, b_bf), idiff(b_cg, b_dh))) { + if (!x) { + tie(a,b,c,d,e,f,g,h) = make_tuple(a,b,a,b,e,f,e,f); + } else { + Int p = x->value(); x = x->next(); + tie(a,b,c,d,e,f,g,h) = make_tuple(a*p+c,b*p+d,a,b,e*p+g,f*p+h,e,f); + } + } else { + if (!y) { + tie(a,b,c,d,e,f,g,h) = make_tuple(a,a,c,c,e,e,g,g); + } else { + Int p = y->value(); y = y->next(); + tie(a,b,c,d,e,f,g,h) = make_tuple(a*p+b,a,c*p+d,c,e*p+f,e,g*p+h,g); + } + } + } + } + virtual Int value() { normalize(); return a/e; } + virtual Pointer next() { + Int r = value(); + if (a-e*r == 0 && b-f*r == 0 && c-g*r == 0 && d-h*r == 0) return 0; + return Pointer(new ArithGen(x,y,e,f,g,h,a-e*r,b-f*r,c-g*r,d-h*r)); + } +}; +struct ContinuedFraction { + using Pointer = shared_ptr; + Pointer head; + ContinuedFraction(Pointer head) : head(head) { } + double toReal() { + Pointer run = head; + function eval = [&](Pointer run, int depth) { + if (!run) return 1.0/0.0; + return run->value() + 1.0 / eval(run->next(), depth-1); + }; + return eval(head, 20); + } +}; +ContinuedFraction operator+(ContinuedFraction x, ContinuedFraction y) { + return ContinuedFraction(ContinuedFraction::Pointer( + new ArithGen(x.head, y.head, 0, 1, 1, 0, 0, 0, 0, 1))); +} +ContinuedFraction operator-(ContinuedFraction x, ContinuedFraction y) { + return ContinuedFraction(ContinuedFraction::Pointer( + new ArithGen(x.head, y.head, 0, 1, -1, 0, 0, 0, 0, 1))); +} +ContinuedFraction operator*(ContinuedFraction x, ContinuedFraction y) { + return ContinuedFraction(ContinuedFraction::Pointer( + new ArithGen(x.head, y.head, 1, 0, 0, 0, 0, 0, 0, 1))); +} +ContinuedFraction operator/(ContinuedFraction x, ContinuedFraction y) { + return ContinuedFraction(ContinuedFraction::Pointer( + new ArithGen(x.head, y.head, 0, 1, 0, 0, 0, 0, 1, 0))); +} +ContinuedFraction operator*(ContinuedFraction x, Int a) { + return ContinuedFraction(ContinuedFraction::Pointer( + new HoloGen(x.head, a, 0, 0, 1))); +} + +int compare(ContinuedFraction x, ContinuedFraction y) { + auto i = x.head, j = y.head; + while (1) { + if (!i && !j) return 0; + if (!j || i->value() < j->value()) return -1; + if (!i || i->value() > j->value()) return +1; + tie(i, j) = make_tuple(j->next(), i->next()); + } +} +bool operator==(ContinuedFraction x, ContinuedFraction y) { return compare(x, y) == 0; } +bool operator!=(ContinuedFraction x, ContinuedFraction y) { return compare(x, y) != 0; } +bool operator<=(ContinuedFraction x, ContinuedFraction y) { return compare(x, y) <= 0; } +bool operator>=(ContinuedFraction x, ContinuedFraction y) { return compare(x, y) >= 0; } +bool operator<(ContinuedFraction x, ContinuedFraction y) { return compare(x, y) < 0; } +bool operator>(ContinuedFraction x, ContinuedFraction y) { return compare(x, y) > 0; } + +struct Rational : ContinuedFraction { + struct RationalGen : Generator { + Int num, den; + RationalGen(Int num, Int den) : num(num), den(den) { } + virtual Int value() { + return num / den - (num < 0); + } + virtual Pointer next() { + if (num - value() * den == 0) return 0; + return Pointer(new RationalGen(den, num - value() * den)); + } + }; + Rational(Int a, Int b) : + ContinuedFraction(Pointer(new RationalGen(a, b))) { } +}; +struct NapierGenerator : Generator { + int i; + NapierGenerator(int i = 0) : i(i) { } + virtual Int value() { + if (i == 0) return 2; + if (i % 3 == 2) return 2 * (i/3) + 2; + return 1; + } + virtual Pointer next() { + return Pointer(new NapierGenerator(value())); + } +}; +ContinuedFraction e(ContinuedFraction::Pointer(new NapierGenerator(0))); + + + +int main() { + ContinuedFraction x = Rational(-8, 11), y = Rational(-1, 2); + cout << compare(x, y) << endl; + cout << x.toReal() << endl; +} From 208bcfd781e9c37aedee117ce45d5a4a5bab5fa1 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Fri, 5 Jan 2018 23:27:52 +0900 Subject: [PATCH 15/80] Rational Type --- math/rational.cc | 157 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 math/rational.cc diff --git a/math/rational.cc b/math/rational.cc new file mode 100644 index 0000000..ad41fc4 --- /dev/null +++ b/math/rational.cc @@ -0,0 +1,157 @@ +// +// Rational Type +// +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +using Int = long long; +struct Rational { + Int num, den; // x = num/den + + static Int gcd(Int a, Int b) { + for (; a; swap(a, b %= a)); + return b; + } + Rational(Int a = 0) : num(a), den(1) { } + Rational(Int a, Int b, bool do_normalize = false) : num(a), den(b) { + if (do_normalize) { + auto g = gcd(num, den); + num /= g; den /= g; + if (den < 0) { num = -num; den = -den; } + } + } + static Rational inf() { return Rational(1,0); } + + Rational inv() const { + if (num < 0) return {-den, -num}; + else return { den, num}; + } + Rational operator+() const { return *this; } + Rational operator-() const { return Rational(-num,den); } + Rational &operator+=(Rational x) { + auto g = gcd(den, x.den); + num = num*(x.den/g) + (den/g)*x.num; + den = den*(x.den/g); + return *this; + } + Rational &operator*=(Rational x) { + auto g = gcd(num, x.den), h = gcd(den, x.num); + num = (num/g)*(x.num/h); + den = (den/h)*(x.den/g); + if (den < 0) { num = -num; den = -den; } + return *this; + } + Rational &operator-=(Rational x) { return *this += -x; } + Rational &operator/=(Rational x) { return *this *= x.inv(); } +}; +Rational operator+(Rational x, Rational y) { return x += y; } +Rational operator-(Rational x, Rational y) { return x -= y; } +Rational operator*(Rational x, Rational y) { return x *= y; } +Rational operator/(Rational x, Rational y) { return x /= y; } +int compare(Rational x, Rational y) { // sign(x-y) + if (x.num == 0) return (y.num < 0) - (y.num > 0); + if (x.num < 0) return y.num >= 0 ? -1 : compare(-y, -x); + if (y.num <= 0) return 1; + while (x.den != 0 && y.den != 0) { + auto a = x.num/x.den, b = y.num/y.den; + if (a != b) return a - b; + swap(x.num -= a*x.den, x.den); + swap(y.num -= b*y.den, y.den); + swap(x, y); + } + if (x.den != 0) return -y.num; + if (y.den != 0) return x.num; + return x.num - y.num; +} +bool operator==(Rational x, Rational y) { return compare(x,y)==0; } +bool operator!=(Rational x, Rational y) { return compare(x,y)!=0; } +bool operator<=(Rational x, Rational y) { return compare(x,y)<=0; } +bool operator>=(Rational x, Rational y) { return compare(x,y)>=0; } +bool operator<(Rational x, Rational y) { return compare(x,y)<0; } +bool operator>(Rational x, Rational y) { return compare(x,y)>0; } + +ostream &operator<<(ostream &os, Rational x) { os<= 0; --i) + swap(num, den += as[i] * num); + if (abs(num) >= 1e9 || abs(den) >= 1e9) { + num = num_; den = den_; + break; // overflow; backup and forgive + } + auto error = fabs(Real(num)/den - a); + b -= as[k]; + if (b == 0 || error < 1e-16) break; + b = 1 / b; + } + if (den < 0) { num = -num; den = -den; } + return Rational(num, den, false); +} + + +/* +Rational approx(Rational::Real a) { + Rational::Int num = 1, den = 0; + Rational::Int as[30] = {0}; + auto b = a; + for (int k = 0; k < 30; ++k) { + as[k] = round(b); + auto numk = num, denk = den; + for (int i = k; i >= 0; --i) + swap(numk, denk += as[i] * numk); + if (abs(numk) >= 1e9 || abs(denk) >= 1e9) break; // overflow + auto error = fabs(Real(numk)/denk - a); + if (error < 1e-9) { + num = numk; den = denk; + break; + } + b -= as[k]; + if (b == 0) break; + b = 1 / b; + } + return Rational(num, den); +} +*/ +void verify_compare() { + srand( time(0) ); + for (int iter = 0; iter < 100000; ++iter) { + cout << iter << endl; + Rational x(rand()%100, 1 + rand()%100); + Rational y(rand()%100, 1 + rand()%100); + assert( (x < y) == (-y < -x) ); + assert( x == x ); + assert( -x == -x ); + assert( (x == y) == (-x == -y) ); + assert( (x != y) == (-x != -y) ); + if (x != Rational(0,1)) assert( (y + x) != (y - x) ); + if (x > Rational(0,1)) assert( (y-x) < (y+x) ); + if (x > Rational(0,1)) assert( (-y-x) < (-y+x) ); + } +} + +int main() { + // 0/1 < 1/1 + /* + Rational x(2,1), y(3,1); + cout << x + y << endl; + */ + /* + Rational x = atan(sqrt(Rational(1,2))); + cout << x << endl; + printf("%.12f\n%.12f\n", (double)x, atan(sqrt(1.0/2.0))); + */ + verify_compare(); +} From bd5aae04b8d8a1c05a894f6223e8df50cdba23a2 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 10:46:25 +0900 Subject: [PATCH 16/80] Bentley-Ottman's Segment Arrangement in O((n+k) log n) --- geometry/segment_arrangement.cc | 340 ++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 geometry/segment_arrangement.cc diff --git a/geometry/segment_arrangement.cc b/geometry/segment_arrangement.cc new file mode 100644 index 0000000..c3233e6 --- /dev/null +++ b/geometry/segment_arrangement.cc @@ -0,0 +1,340 @@ +// +// Segment Arrangement (Bentley-Ottman's Plane-Sweep) +// +// Description: +// Given a set of segments, it finds the all intersections of the +// segments and construct the graph structure. By the plane-sweep +// with the balanced binary search tree, it runs in O(k log n) +// time, where k is the size of output. +// +// Complexity: +// O(k log n), where k is the size of input. +// +// References: +// CGAA +// + +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +using Real = double; +const Real EPS = 1e-8; +int sign(Real x) { return (x > EPS) - (x < -EPS); } +struct Point { + Real x, y; + Point &operator+=(Point p) { x += p.x; y += p.y; return *this; } + Point &operator*=(Real a) { x *= a; y *= a; return *this; } + Point operator+() const { return {+x, +y}; } + Point operator-() const { return {-x, -y}; } + + Point &operator-=(Point p) { return *this += -p; } + Point &operator/=(Real a) { return *this *= 1/a; } +}; +Point operator+(Point p, Point q) { return p += q; } +Point operator-(Point p, Point q) { return p -= q; } +Point operator*(Real a, Point p) { return p *= a; } +Point operator*(Point p, Real a) { return p *= a; } +Point operator/(Point p, Real a) { return p /= a; } + +int compare(Point p, Point q) { + int s = sign(p.x - q.x); + return s ? s : sign(p.y - q.y); +} +bool operator==(Point p, Point q) { return compare(p,q)==0; } +bool operator!=(Point p, Point q) { return compare(p,q)!=0; } +bool operator<=(Point p, Point q) { return compare(p,q)<=0; } +bool operator>=(Point p, Point q) { return compare(p,q)>=0; } +bool operator<(Point p, Point q) { return compare(p,q)<0; } +bool operator>(Point p, Point q) { return compare(p,q)>0; } + +Real dot(Point p, Point q) { return p.x*q.x+p.y*q.y; } +Real cross(Point p, Point q) { return p.x*q.y-p.y*q.x; } // left turn > 0 +Real norm2(Point p) { return dot(p,p); } +Point orth(Point p) { return {-p.y, p.x}; } +Real norm(Point p) { return sqrt(dot(p,p)); } +Real arg(Point p) { return atan2(p.y, p.x); } +Real arg(Point p, Point q){ return atan2(cross(p,q), dot(p,q)); } + +istream &operator>>(istream &is, Point &p) { is>>p.x>>p.y;return is; } +ostream &operator<<(ostream &os, const Point &p) { os<<"("< intersect(Segment s, Segment t) { + auto a = cross(s.q - s.p, t.q - t.p); + auto b = cross(t.p - s.p, t.q - t.p); + auto c = cross(s.q - s.p, s.p - t.p); + if (a < 0) { a = -a; b = -b; c = -c; } + if (sign(b) < 0 || sign(a-b) < 0 || + sign(c) < 0 || sign(a-c) < 0) return {}; // disjoint + if (sign(a) != 0) return {s.p + b/a*(s.q - s.p)}; // properly crossing + vector ps; // same line + auto insert_if_possible = [&](Point p) { + for (auto q: ps) if (sign(dot(p-q, p-q)) == 0) return; + ps.push_back(p); + }; + if (sign(dot(s.p-t.p, s.q-t.p)) <= 0) insert_if_possible(t.p); + if (sign(dot(s.p-t.q, s.q-t.q)) <= 0) insert_if_possible(t.q); + if (sign(dot(t.p-s.p, t.q-s.p)) <= 0) insert_if_possible(s.p); + if (sign(dot(t.p-s.q, t.q-s.q)) <= 0) insert_if_possible(s.q); + return ps; +} + +struct DoublyConnectedEdgeList { + struct Vertex { int edge; }; // incident + struct Edge { int vertex, twin, prev, next, face; }; // origin, twin, incident list, left face + struct Face { int edge; }; // any incident face + vector point; + vector vertex; + vector edge; + vector face; + int newVertex(Point p, int e = -1) { + point.push_back(p); + vertex.push_back({e}); + return vertex.size()-1; + } + int newEdge(int vertex = -1) { + edge.push_back({vertex, -1, -1, -1, -1}); + return edge.size()-1; + } + void completeFaces() { + face.clear(); + for (int e = 0; e < edge.size(); ++e) edge[e].face = -1; + for (int e = 0; e < edge.size(); ++e) { + if (edge[e].face >= 0) continue; + int f = face.size(); + face.push_back({e}); + int x = e; + do { + edge[x].face = f; + x = edge[edge[x].twin].prev; + } while (x != e); + } + } +}; + +struct Arrangement : DoublyConnectedEdgeList { + vector segs; + + unordered_map> adj; // (Vertex, Vertex) -> Edge + + struct Node { // Sweep-Line Structure (RBST) + int index, size = 1; + Node *left = 0, *right = 0; + } *root = 0; + vector ns; + Node *update(Node *x) { + if (x) { + x->size = 1; + if (x->left) x->size += x->left->size; + if (x->right) x->size += x->right->size; + } + return x; + } + Node *merge(Node *x, Node *y) { + if (!x) return y; + if (!y) return x; + if (rand() % (x->size + y->size) < x->size) { + x->right = merge(x->right, y); + return update(x); + } else { + y->left = merge(x, y->left); + return update(y); + } + } + template // 3-way split: cond(x) < 0, cond(x) == 0, cond(x) > 0 + tuple split(Node *x, C cond) { + if (!x) return make_tuple(x,x,x); + if (cond(x) == 0) { + auto a = split(x->left, cond); + auto b = split(x->right, cond); + x->left = x->right = 0; update(x); + get<1>(a) = merge(merge(get<1>(a), x), get<1>(b)); + get<2>(a) = get<2>(b); + return a; + } + if (cond(x) < 0) { + auto a = split(x->right, cond); + x->right = 0; update(x); + get<0>(a) = merge(x, get<0>(a)); + return a; + } + if (cond(x) > 0) { + auto a = split(x->left, cond); + x->left = 0; update(x); + get<2>(a) = merge(get<2>(a), x); + return a; + } + } + Node *leftmost(Node *x) { while (x && x->left) x = x->left; return x; } + Node *rightmost(Node *x) { while (x && x->right) x = x->right; return x; } + template + void process(Node *x, F func) { + if (!x) return; + process(x->left, func); + func(x); + process(x->right, func); + } + + Arrangement(vector segs_) : segs(segs_) { + ns.resize(segs.size()); + set events; + map> L, R; + + for (int i = 0; i < segs.size(); ++i) { + if (segs[i].q < segs[i].p) swap(segs[i].p, segs[i].q); + events.insert(segs[i].p); + events.insert(segs[i].q); + L[segs[i].p].insert(i); + R[segs[i].q].insert(i); + ns[i].index = i; + } + vector last(segs.size(), -1); + + while (!events.empty()) { + const Point p = *events.begin(); + events.erase(events.begin()); + int u = newVertex(p); + + auto cond = [&](Node *x) { + const Segment &s = segs[x->index]; + if (sign(s.q.x - s.p.x) == 0) { + if (sign(p.y - s.p.y) < 0) return -1; + if (sign(s.q.y - p.y) < 0) return +1; + return 0; + } + return -sign(cross(s.p - p, s.q - p)); + }; + auto z = split(root, cond); + vector inserter; + process(get<1>(z), [&](Node *x) { + int v = last[x->index]; + if (!adj[u].count(v)) { + int e = newEdge(u), f = newEdge(v); + adj[u][v] = e; + adj[v][u] = f; + edge[e].twin = f; + edge[f].twin = e; + } + if (!R[p].count(x->index)) + inserter.push_back(x); + }); + for (int i: L[p]) + if (!R[p].count(i)) + inserter.push_back(&ns[i]); + sort(all(inserter), [&](Node *x, Node *y) { + const Segment &s = segs[x->index], &t = segs[y->index]; + return sign(cross(s.q - s.p, t.q - t.p)) >= 0; + }); + auto addEvent = [&](Node *x, Node *y) { + if (!x || !y) return; + vector ps = intersect(segs[x->index], segs[y->index]); + for (Point q: ps) + if (p < q) events.insert(q); + }; + if (inserter.empty()) { + addEvent(rightmost(get<0>(z)), leftmost(get<2>(z))); + } else { + addEvent(rightmost(get<0>(z)), inserter[0]); + addEvent(leftmost(get<2>(z)), inserter.back()); + } + root = 0; + for (int i = 0; i < inserter.size(); ++i) { + last[inserter[i]->index] = u; + inserter[i]->left = inserter[i]->right = 0; + root = merge(root, update(inserter[i])); + } + root = merge(merge(get<0>(z), root), get<2>(z)); + } + for (auto &pp: adj) { + int u = pp.fst; + vector es; + for (auto z: pp.snd) es.push_back(z.snd); + sort(all(es), [&](int e, int f) { + auto quad = [](Point p) { + for (int i = 1; i <= 4; ++i, swap(p.x = -p.x, p.y)) + if (p.x > 0 && p.y >= 0) return i; + return 0; + }; + const Point p = point[edge[edge[e].twin].vertex] - point[edge[e].vertex]; + const Point q = point[edge[edge[f].twin].vertex] - point[edge[f].vertex]; + if (quad(p) != quad(q)) return quad(p) < quad(q); + return sign(cross(p, q)) > 0; + }); + vertex[u].edge = es.back(); + for (int e: es) { + edge[vertex[u].edge].next = e; + edge[edge[vertex[u].edge].next].prev = vertex[u].edge; + vertex[u].edge = edge[vertex[u].edge].next; + } + } + } +}; +void AOJ1226() { + for (int n; ~scanf("%d", &n) && n; ) { + vector> a(4, vector(n)); + for (int k = 0; k < 4; ++k) + for (int i = 0; i < n; ++i) + scanf("%lf", &a[k][i]); + + vector ss = { + {{0,0},{0,1}}, + {{0,1},{1,1}}, + {{1,1},{1,0}}, + {{1,0},{0,0}} + }; + for (int i = 0; i < n; ++i) { + ss.push_back({{a[0][i],0},{a[1][i],1}}); + ss.push_back({{0,a[2][i]},{1,a[3][i]}}); + } + Arrangement arr(ss); + arr.completeFaces(); + + double result = 0; + for (int f = 0; f < arr.face.size(); ++f) { + double area = 0; + int e = arr.face[f].edge; + do { + area += cross(arr.point[arr.edge[e].vertex], + arr.point[arr.edge[arr.edge[e].twin].vertex]), + e = arr.edge[arr.edge[e].twin].prev; + } while (e != arr.face[f].edge); + result = max(result, area); + } + printf("%.6f\n", result/2); + } +} +void AOJ2448() { + int n; scanf("%d", &n); + vector ps(n); + for (int i = 0; i < n; ++i) + scanf("%lf %lf", &ps[i].x, &ps[i].y); + vector ss; + for (int i = 0; i+1 < n; ++i) + ss.push_back({ps[i], ps[i+1]}); + Arrangement arr(ss); + arr.completeFaces(); + + double result = 0; + for (int f = 0; f < arr.face.size(); ++f) { + double area = 0; + int e = arr.face[f].edge; + do { + area += cross(arr.point[arr.edge[e].vertex], + arr.point[arr.edge[arr.edge[e].twin].vertex]), + e = arr.edge[arr.edge[e].twin].prev; + } while (e != arr.face[f].edge); + if (area > 0) result += area; + } + printf("%.12f\n", result/2); +} + +int main() { + // AOJ2448(); + AOJ1226(); +} + From 2ac92f9f738aaa7cf935ffc05792c19c7ee90eb0 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 10:48:38 +0900 Subject: [PATCH 17/80] Bentley-Ottman's Segment Arrangement in O(k log n). --- geometry/segment_arrangement.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/geometry/segment_arrangement.cc b/geometry/segment_arrangement.cc index c3233e6..7d3f8ab 100644 --- a/geometry/segment_arrangement.cc +++ b/geometry/segment_arrangement.cc @@ -10,6 +10,10 @@ // Complexity: // O(k log n), where k is the size of input. // +// Verified: +// AOJ 1226 +// AOJ 2448 +// // References: // CGAA // From dd674a0b8ea0ac7bb6f8108f138fc94007ad10f1 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 12:01:07 +0900 Subject: [PATCH 18/80] Bentley-Ottman Segment Arrangement --- geometry/segment_arrangement.cc | 111 ++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/geometry/segment_arrangement.cc b/geometry/segment_arrangement.cc index 7d3f8ab..86d2212 100644 --- a/geometry/segment_arrangement.cc +++ b/geometry/segment_arrangement.cc @@ -88,34 +88,47 @@ vector intersect(Segment s, Segment t) { return ps; } + +// +// each vertex has one incident edge +// each edge has the origin vertex, twin edge, and the left face. +// two edges (prev, next) representing a list of edges surrounding a face. +// struct DoublyConnectedEdgeList { - struct Vertex { int edge; }; // incident - struct Edge { int vertex, twin, prev, next, face; }; // origin, twin, incident list, left face - struct Face { int edge; }; // any incident face + vector incident_edge; // vertex + vector origin, twin, prev, next, incident_face; // edge + vector component; // face + int edges() const { return origin.size(); } + int vertices() const { return incident_edge.size(); } + int faces() const { return component.size(); } + vector point; - vector vertex; - vector edge; - vector face; int newVertex(Point p, int e = -1) { point.push_back(p); - vertex.push_back({e}); - return vertex.size()-1; + incident_edge.push_back(e); + return vertices()-1; + } + int newEdge(int o = -1) { + origin.push_back(o); + twin.push_back(-1); + prev.push_back(-1); + next.push_back(-1); + incident_face.push_back(-1); + return edges()-1; } - int newEdge(int vertex = -1) { - edge.push_back({vertex, -1, -1, -1, -1}); - return edge.size()-1; + int newFace(int e = -1) { + component.push_back(e); + return component.size()-1; } void completeFaces() { - face.clear(); - for (int e = 0; e < edge.size(); ++e) edge[e].face = -1; - for (int e = 0; e < edge.size(); ++e) { - if (edge[e].face >= 0) continue; - int f = face.size(); - face.push_back({e}); - int x = e; + component.clear(); + fill(all(incident_face), -1); + for (int e = 0; e < edges(); ++e) { + if (incident_face[e] >= 0) continue; + int f = newFace(e), x = e; do { - edge[x].face = f; - x = edge[edge[x].twin].prev; + incident_face[x] = f; + x = next[x]; } while (x != e); } } @@ -183,7 +196,6 @@ struct Arrangement : DoublyConnectedEdgeList { func(x); process(x->right, func); } - Arrangement(vector segs_) : segs(segs_) { ns.resize(segs.size()); set events; @@ -213,7 +225,7 @@ struct Arrangement : DoublyConnectedEdgeList { } return -sign(cross(s.p - p, s.q - p)); }; - auto z = split(root, cond); + auto z = split(root, cond); vector inserter; process(get<1>(z), [&](Node *x) { int v = last[x->index]; @@ -221,8 +233,8 @@ struct Arrangement : DoublyConnectedEdgeList { int e = newEdge(u), f = newEdge(v); adj[u][v] = e; adj[v][u] = f; - edge[e].twin = f; - edge[f].twin = e; + twin[e] = f; + twin[f] = e; } if (!R[p].count(x->index)) inserter.push_back(x); @@ -230,6 +242,7 @@ struct Arrangement : DoublyConnectedEdgeList { for (int i: L[p]) if (!R[p].count(i)) inserter.push_back(&ns[i]); + sort(all(inserter), [&](Node *x, Node *y) { const Segment &s = segs[x->index], &t = segs[y->index]; return sign(cross(s.q - s.p, t.q - t.p)) >= 0; @@ -254,28 +267,33 @@ struct Arrangement : DoublyConnectedEdgeList { } root = merge(merge(get<0>(z), root), get<2>(z)); } + vector next_inc(next.size()), prev_inc(prev.size()); for (auto &pp: adj) { int u = pp.fst; vector es; for (auto z: pp.snd) es.push_back(z.snd); - sort(all(es), [&](int e, int f) { + sort(all(es), [&](int e, int f) { // polar sort auto quad = [](Point p) { for (int i = 1; i <= 4; ++i, swap(p.x = -p.x, p.y)) if (p.x > 0 && p.y >= 0) return i; return 0; }; - const Point p = point[edge[edge[e].twin].vertex] - point[edge[e].vertex]; - const Point q = point[edge[edge[f].twin].vertex] - point[edge[f].vertex]; + const Point p = point[origin[twin[e]]] - point[origin[e]]; + const Point q = point[origin[twin[f]]] - point[origin[f]]; if (quad(p) != quad(q)) return quad(p) < quad(q); return sign(cross(p, q)) > 0; }); - vertex[u].edge = es.back(); - for (int e: es) { - edge[vertex[u].edge].next = e; - edge[edge[vertex[u].edge].next].prev = vertex[u].edge; - vertex[u].edge = edge[vertex[u].edge].next; + incident_edge[u] = es[0]; + for (int i = 0; i < es.size(); ++i) { + int j = (i + 1) % es.size(); + next_inc[es[i]] = es[j]; + prev_inc[es[j]] = es[i]; } } + for (int e = 0; e < edges(); ++e) { + next[e] = prev_inc[twin[e]]; + prev[e] = twin[next_inc[e]]; + } } }; void AOJ1226() { @@ -299,14 +317,14 @@ void AOJ1226() { arr.completeFaces(); double result = 0; - for (int f = 0; f < arr.face.size(); ++f) { + for (int f = 0; f < arr.faces(); ++f) { double area = 0; - int e = arr.face[f].edge; + int e = arr.component[f]; do { - area += cross(arr.point[arr.edge[e].vertex], - arr.point[arr.edge[arr.edge[e].twin].vertex]), - e = arr.edge[arr.edge[e].twin].prev; - } while (e != arr.face[f].edge); + area += cross(arr.point[arr.origin[e]], + arr.point[arr.origin[arr.twin[e]]]), + e = arr.next[e]; + } while (e != arr.component[f]); result = max(result, area); } printf("%.6f\n", result/2); @@ -324,21 +342,20 @@ void AOJ2448() { arr.completeFaces(); double result = 0; - for (int f = 0; f < arr.face.size(); ++f) { + for (int f = 0; f < arr.faces(); ++f) { double area = 0; - int e = arr.face[f].edge; + int e = arr.component[f]; do { - area += cross(arr.point[arr.edge[e].vertex], - arr.point[arr.edge[arr.edge[e].twin].vertex]), - e = arr.edge[arr.edge[e].twin].prev; - } while (e != arr.face[f].edge); + area += cross(arr.point[arr.origin[e]], + arr.point[arr.origin[arr.twin[e]]]), + e = arr.next[e]; + } while (e != arr.component[f]); if (area > 0) result += area; } printf("%.12f\n", result/2); } int main() { - // AOJ2448(); - AOJ1226(); + AOJ2448(); + //AOJ1226(); } - From 995cf29c0f28e583e31e928d4f4d2b9f301b53a5 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 02:19:35 -0600 Subject: [PATCH 19/80] Coordinate Domination --- geometry/coordinate_domination.cc | 89 +++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 geometry/coordinate_domination.cc diff --git a/geometry/coordinate_domination.cc b/geometry/coordinate_domination.cc new file mode 100644 index 0000000..ec23e35 --- /dev/null +++ b/geometry/coordinate_domination.cc @@ -0,0 +1,89 @@ +// +// 3D Coordinate-Wise Domination +// +// Description: +// +// Point (x,y,z) dominates (x',y',z') if +// x < x', y < y', and z < z' +// holds. Kung-Luccio-Preparata proposed an algorithm to compute +// the all set of dominating points in O(n log n) time. +// +// It maintains a data structure to check the domination efficiently +// in (y,z)-plane, and proceed the points in the decreasing order of x. +// By the processing order, the new point is only dominated by the +// previously processed points. Also, by the processing order, the +// new point is dominated if its (y,z) coordinate is dominated. +// By using a data structure, it is efficiently checked. +// +// Complexity: +// +// O(n log n). By using this method recursively, +// we can solve d-dimensional domination in O(n log^{d-2} n). +// +// Reference: +// +// Hsiang-Tsung Kung, Fabrizio Luccio, Franco P. Preparata (1975): +// "On finding the maxima of a set of vectors." Journal of the ACM, +// vol.22, no.4, pp.469-476. +// + +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +using Real = int; +struct Point { + Real x, y, z; + bool operator < (Point p) const { + if (x != p.x) return x < p.x; + if (y != p.y) return y < p.y; + return z < p.z; + } +}; +int domination(vector xs) { + multiset frontier; + auto bad = [&](multiset::iterator it) { + auto jt = next(it); + if (jt == frontier.end()) return false; + return it->y < jt->y && it->z < jt->z; + }; + int n = xs.size(); + sort(all(xs)); + int count = 0; + for (int i = n-1; i >= 0; --i) { + auto proc = [&] { + if (i < n-1 && xs[i].x == xs[i+1].x) return true; + Point p(xs[i]); p.x = 0; + auto it = frontier.insert(p); + if (bad(it)) { + frontier.erase(it); + return false; + } else { + while (it != frontier.begin() && bad(prev(it))) + frontier.erase(prev(it)); + return true; + } + }; + if (proc()) ++count; + } + return count; +} + + +int main() { + int ncase; scanf("%d", &ncase); + for (int icase = 0; icase < ncase; ++icase) { + int n; scanf("%d", &n); + vector ps(n); + for (int i = 0; i < n; ++i) { + scanf("%d %d %d", &ps[i].x, &ps[i].y, &ps[i].z); + ps[i].x = -ps[i].x; + ps[i].y = -ps[i].y; + ps[i].z = -ps[i].z; + } + printf("%d\n", domination(ps)); + } +} From 5ec70c5deb385040459a9d7002587518f1242d43 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 02:21:44 -0600 Subject: [PATCH 20/80] Coordinate Domination --- geometry/coordinate_domination.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/geometry/coordinate_domination.cc b/geometry/coordinate_domination.cc index ec23e35..6bbb728 100644 --- a/geometry/coordinate_domination.cc +++ b/geometry/coordinate_domination.cc @@ -8,12 +8,11 @@ // holds. Kung-Luccio-Preparata proposed an algorithm to compute // the all set of dominating points in O(n log n) time. // -// It maintains a data structure to check the domination efficiently -// in (y,z)-plane, and proceed the points in the decreasing order of x. -// By the processing order, the new point is only dominated by the -// previously processed points. Also, by the processing order, the -// new point is dominated if its (y,z) coordinate is dominated. -// By using a data structure, it is efficiently checked. +// It maintains a data structure to check the domination in (y,z) plane, +// and proceed the points in the decreasing order of x. +// By the processing order, the new point is never dominated by the latter +// points and is dominated by the previous points if its (y,z) coordinate +// is dominated by them. // // Complexity: // From d09957fd1384463f339e3c682f2e3346d0fc136b Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 14:49:58 -0600 Subject: [PATCH 21/80] Suffix Automaton --- string/suffix_automaton.cc | 149 +++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 string/suffix_automaton.cc diff --git a/string/suffix_automaton.cc b/string/suffix_automaton.cc new file mode 100644 index 0000000..a4315f8 --- /dev/null +++ b/string/suffix_automaton.cc @@ -0,0 +1,149 @@ +// +// Suffix Automaton +// +// Description: +// +// It is an automaton that accepts all suffixes +// of a given string. +// +// Verified: +// +// SPOJ_SUBST1 +// SPOJ_SUBLEX +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +struct SuffixAutomaton { + vector length = {0}, parent = {-1}; + vector> next = {map()}; + int size() const { return next.size(); } + + int extend(int p, int c) { + int head = next.size(); + next.push_back(map()); + length.push_back(length[p]+1); + parent.push_back(0); + while (p >= 0 && !next[p].count(c)) { + next[p][c] = head; + p = parent[p]; + } + if (p >= 0) { + int q = next[p][c]; + if (length[p]+1 == length[q]) { + parent[head] = q; + } else { + int clone = next.size(); // clone of q + length.push_back(length[p]+1); + parent.push_back(parent[q]); + next.push_back(next[q]); + parent[q] = parent[head] = clone; + while (p >= 0 && next[p][c] == q) { + next[p][c] = clone; + p = parent[p]; + } + } + } + return head; + } + vector topological_order; + SuffixAutomaton(const char s[]) { // can be constructed online + int p = 0; + for(int i = 0; s[i]; ++i) + p = extend(p, s[i]); + vector mark(size()); // topological sort + for (int i = 0; i < size(); ++i) + for (auto z: next[i]) ++mark[z.snd]; + topological_order.push_back(0); + for (int i = 0; i < size(); ++i) + for (auto z: next[i]) + if (--mark[z.snd] == 0) topological_order.push_back(z.snd); + } + template // topological order + void process(F func) { + for (int i = 0; i < topological_order.size(); ++i) func(i); + } + template + void processRev(F func) { // reverse topological order + for (int i = topological_order.size()-1; i >= 0; --i) func(i); + } +}; +int countDistinctSubstrings(const char s[]) { + SuffixAutomaton M(s); + vector dp(M.size()); + int ans = 0; + dp[0] = 1; + M.process([&](int i) { + ans += dp[i]; + for (auto z: M.next[i]) + dp[z.snd] += dp[i]; + }); + return ans; +} + +string kthSubstring(const char s[], int k) { + SuffixAutomaton M(s); + vector num_terminal(M.size()); // preprocess that can be reused + M.processRev([&](int i) { + num_terminal[i] = 1; + for (auto z: M.next[i]) + num_terminal[i] += num_terminal[z.snd]; + }); + string ret; // answer to query + for (int p = 0; k-- > 0; ) { + for (auto z: M.next[p]) { + if (num_terminal[z.snd] > k) { + ret.push_back(z.fst); + p = z.snd; break; + } else k -= num_terminal[z.snd]; + } + } + return ret; +} + +void SPOJ_SUBST1() { + int ncase; + scanf("%d", &ncase); + for (int icase = 0; icase < ncase; ++icase) { + char s[100000]; + scanf("%s", s); + printf("%d\n", countDistinctSubstrings(s)-1); + } +} +void SPOJ_SUBLEX() { + char s[100000]; + scanf("%s", s); + SuffixAutomaton M(s); + + vector num_terminal(M.size()); + M.processRev([&](int i) { + num_terminal[i] = 1; + for (auto z: M.next[i]) + num_terminal[i] += num_terminal[z.snd]; + }); + int ncase; scanf("%d", &ncase); + for (int icase = 0; icase < ncase; ++icase) { + int k; scanf("%d", &k); + string ret; + for (int p = 0; k-- > 0; ) { + for (auto z: M.next[p]) { + if (num_terminal[z.snd] > k) { + ret.push_back(z.fst); + p = z.snd; break; + } else k -= num_terminal[z.snd]; + } + } + printf("%s\n", ret.c_str()); + } +} + +int main() { + //SPOJ_SUBST1(); + SPOJ_SUBLEX(); +} From 938aa43edfcdbab6591ea599858ad23ac4b5ce36 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 15:10:26 -0600 Subject: [PATCH 22/80] Suffix Automaton --- string/suffix_automaton.cc | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/string/suffix_automaton.cc b/string/suffix_automaton.cc index a4315f8..8e6e64a 100644 --- a/string/suffix_automaton.cc +++ b/string/suffix_automaton.cc @@ -1,16 +1,33 @@ // -// Suffix Automaton +// Suffix Automaton (aka. Suffix Directed Acyclic Word Graph) // // Description: // // It is an automaton that accepts all suffixes -// of a given string. +// of a given string. It can be constructed in O(n) time +// by using Blumer et al.'s online construction. +// +// Note that a factor automaton is obtained from +// the suffix automaton by setting the all states as terminal. +// +// Complexity: +// O(n) // // Verified: // // SPOJ_SUBST1 // SPOJ_SUBLEX // +// References: +// A. Blumer, J. Blumer, D. Haussler, A. Ehrenfeucht, +// M. T. Chen, and J. Seiferas (1985): +// The smallest automation recognizing the subwords of a text. +// Theoretical Computer Science, vol. 40, pp. 31-55. +// +// M. Crochemore, and W. Rytter (1994): +// Text Algorithms. +// Oxford University Press. +// #include using namespace std; @@ -21,32 +38,33 @@ using namespace std; #define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } struct SuffixAutomaton { - vector length = {0}, parent = {-1}; + vector length = {0}; // maximum length corresponding to the node + vector link = {-1}; // suffix link of the node vector> next = {map()}; int size() const { return next.size(); } int extend(int p, int c) { int head = next.size(); - next.push_back(map()); length.push_back(length[p]+1); - parent.push_back(0); + link.push_back(0); + next.push_back(map()); while (p >= 0 && !next[p].count(c)) { next[p][c] = head; - p = parent[p]; + p = link[p]; } if (p >= 0) { int q = next[p][c]; if (length[p]+1 == length[q]) { - parent[head] = q; + link[head] = q; } else { int clone = next.size(); // clone of q length.push_back(length[p]+1); - parent.push_back(parent[q]); + link.push_back(link[q]); next.push_back(next[q]); - parent[q] = parent[head] = clone; + link[q] = link[head] = clone; while (p >= 0 && next[p][c] == q) { next[p][c] = clone; - p = parent[p]; + p = link[p]; } } } From ee9c42395f8673b4eed5b3918ce8c1fa463e85b8 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 15:10:45 -0600 Subject: [PATCH 23/80] Delete factor_automaton.cc --- string/factor_automaton.cc | 134 ------------------------------------- 1 file changed, 134 deletions(-) delete mode 100644 string/factor_automaton.cc diff --git a/string/factor_automaton.cc b/string/factor_automaton.cc deleted file mode 100644 index 6b38e12..0000000 --- a/string/factor_automaton.cc +++ /dev/null @@ -1,134 +0,0 @@ -// -// Factor Automaton (aka. Directed Acyclig Word Graph) -// -// Description: -// For a given string s, the factor automaton is an -// automaton that accepts all substrings s[i,j). -// The automaton has O(n) state. -// -// Algorithm: -// Blumer et al's online construction. -// See Section 6.3 of Crochemore-Rytter. -// -// Complexity: -// O(n) time and space. -// -// Verified: -// SPOJ 22531 -// -// References: -// A. Blumer, J. Blumer, D. Haussler, A. Ehrenfeucht, -// M. T. Chen, and J. Seiferas (1985): -// The smallest automation recognizing the subwords of a text. -// Theoretical Computer Science, vol. 40, pp. 31-55. -// -// M. Crochemore, and W. Rytter (1994): -// Text Algorithms. -// Oxford University Press. -// -#include -#include -#include -#include -#include -#include - -using namespace std; - -#define fst first -#define snd second -#define all(c) ((c).begin()), ((c).end()) - -struct factor_automaton { - vector> link; - vector> bold; - vector suf; - int root; - int add_node() { - link.push_back(vector(0x100)); - bold.push_back(vector(0x100)); - suf.push_back(0); - return link.size()-1; - } - factor_automaton(const char s[]) { - add_node(); // = nil - int sink = root = add_node(); - suf[root] = 0; - for (int i = 0; s[i]; ++i) { - char a = s[i]; - int newsink = add_node(); - link[sink][a] = newsink; - bold[sink][a] = true; - int w = suf[sink]; - while (w != 0 && link[w][a] == 0) { - link[w][a] = newsink; - bold[w][a] = false; - w = suf[w]; - } - int v = link[w][a]; - if (w == 0) suf[newsink] = root; - else if (bold[w][a]) suf[newsink] = v; - else { - int newnode = add_node(); - link[newnode] = link[v]; - link[w][a] = newnode; - bold[w][a] = true; - suf[newsink] = newnode; - suf[newnode] = suf[v]; - suf[v] = newnode; - w = suf[w]; - while (w != 0 && link[w][a] == v && bold[w][a] == false) { - link[w][a] = newnode; - w = suf[w]; - } - } - sink = newsink; - } - } - - void disp() { - cout << "--- display ---" << endl; - for (int i = 1; i < link.size(); ++i) { - cout << " state " << i << endl; - for (int c = 0; c < 0x100; ++c) { - if (link[i][c] > 0) { - if (bold[i][c]) cout << " ==" << (char)c << "==> " << link[i][c] << endl; - else cout << " --" << (char)c << "--> " << link[i][c] << endl; - } - } - } - - } -}; - - -int main() { - for (char text[100000]; ~scanf("%s", text); ) { - factor_automaton FA(text); - - vector> prev(FA.link.size()); - queue que; - que.push(FA.root); - while (prev[0].fst == 0) { - int s = que.front(); - que.pop(); - for (int c = 'A'; c <= 'Z'; ++c) { - //for (int c = 'A'; c <= 'B'; ++c) { // for test - if (prev[FA.link[s][c]].fst == 0) { - prev[FA.link[s][c]] = {s, c}; - que.push(FA.link[s][c]); - //cout << s << " --" << (char)c << "--> " << FA.link[s][c] << endl; - } - } - } - int s = 0; - vector result; - do { - result.push_back(prev[s].snd); - s = prev[s].fst; - } while (s > 1); - for (int i = result.size()-1; i >= 0; --i) - printf("%c", result[i]); - printf("\n"); - } -} From b6dcd8f85e16e856bf777586bce4fcbe5250a5ac Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 16:50:06 -0600 Subject: [PATCH 24/80] Automatic Differentiation by Dual Numbers --- math/dual_number.cc | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 math/dual_number.cc diff --git a/math/dual_number.cc b/math/dual_number.cc new file mode 100644 index 0000000..33fe5df --- /dev/null +++ b/math/dual_number.cc @@ -0,0 +1,58 @@ +// +// Automatic Differentiation by Dual Numbers +// +// Description: +// +// Dual number is an extended real number of the form a + epsilon b, +// where epsilon is the first order infinitesimal (i.e, epsilon^2 = 0). +// By overloading each function for dual numbers, as in the code, +// we can obtain the derivative of f at a by evaluating f(a + epsilon). +// +// Complexity: +// +// Linear in composition depth. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +using Real = double; +struct DualNumber { + Real a, b; // a + epsilon b + DualNumber(Real a = 0, Real b = 0) : a(a), b(b) { } + DualNumber &operator+=(DualNumber x) { b+=x.b; a+=x.a; return *this; } + DualNumber &operator*=(DualNumber x) { b=b*x.a+a*x.b; a*=x.a; return *this; } + DualNumber operator+() const { return *this; } + DualNumber operator-() const { return {-a, -b}; } + DualNumber inv() const { return {1.0/a, -b/(a*a)}; } + DualNumber &operator-=(DualNumber x) { return *this += -x; } + DualNumber &operator/=(DualNumber x) { return *this *= x.inv(); } +}; +DualNumber operator+(DualNumber x, DualNumber y) { return x += y; } +DualNumber operator-(DualNumber x, DualNumber y) { return x -= y; } +DualNumber operator*(DualNumber x, DualNumber y) { return x *= y; } +DualNumber operator/(DualNumber x, DualNumber y) { return x /= y; } + +// define functions with its derivative +DualNumber pow(DualNumber x, Real e) { return {pow(x.a,e),x.b*pow(x.a,e-1)}; } +DualNumber sqrt(DualNumber x) { return pow(x,0.5); } +DualNumber exp(DualNumber x) { return {exp(x.a),x.b*exp(x.a)}; } +DualNumber cos(DualNumber x) { return {cos(x.a),-x.b*sin(x.a)}; } +DualNumber sin(DualNumber x) { return {sin(x.a),x.b*cos(x.a)}; } +DualNumber tan(DualNumber x) { return sin(x)/cos(x); } +DualNumber log(DualNumber x) { return {log(x.a),x.b/x.a}; } + +int main() { + DualNumber x = 3, y = 4; + auto f = [&](DualNumber x) { + return sin(x*x) + cos(exp(x)) + tan(x); + }; + x.b = 1; // set infinitesimal part + cout << f(x).b << endl; +} + From b7238a5f95fc7e1200e8323f990e6af7fdab0f0d Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 16:56:22 -0600 Subject: [PATCH 25/80] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 6200a46..3e06bb1 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,9 @@ You can use the codes for *any purpose without any warranty*. author: Takanori MAEHARA (web: http://www.prefield.com, e-mail: maehara@prefield.com, twitter: @tmaehara) + +# Recruiting + +I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute on artificial intelligence related areas. In particular, I am working on discrete algorithms (including topics in this github repository). + +We are hiring highly-skilled programmers and researchers who are interested in working with us. If you are intersted in, please feel free to contact me by email or something else. From ea229deec65b5b0070efb3c80f407250d28432c6 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 17:03:31 -0600 Subject: [PATCH 26/80] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e06bb1..c225377 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,7 @@ author: Takanori MAEHARA (web: http://www.prefield.com, e-mail: maehara@prefield I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute on artificial intelligence related areas. In particular, I am working on discrete algorithms (including topics in this github repository). -We are hiring highly-skilled programmers and researchers who are interested in working with us. If you are intersted in, please feel free to contact me by email or something else. +We are hiring programmers and researchers. The criterion is, basically, +- Programmer: having some strong achievements in some competitions (e.g., ICPC, Google Code Jam, CodeForces, etc...). +- Researcher: having some strong publications in some CS area (e.g., SODA/ICALP/IPCO, NIPS/ICML, AAAI/IJCAI, etc...). +If you are interested in, please feel free to contact me. From 5c5402934bca270232df4623b73d36f5cb2e22ca Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 17:06:00 -0600 Subject: [PATCH 27/80] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index c225377..8700f08 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,4 @@ author: Takanori MAEHARA (web: http://www.prefield.com, e-mail: maehara@prefield # Recruiting -I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute on artificial intelligence related areas. In particular, I am working on discrete algorithms (including topics in this github repository). - -We are hiring programmers and researchers. The criterion is, basically, -- Programmer: having some strong achievements in some competitions (e.g., ICPC, Google Code Jam, CodeForces, etc...). -- Researcher: having some strong publications in some CS area (e.g., SODA/ICALP/IPCO, NIPS/ICML, AAAI/IJCAI, etc...). -If you are interested in, please feel free to contact me. +I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute on artificial intelligence related areas. In particular, I am working on discrete algorithms (including topics in this github repository). We are hiring *strong programmers/researchers* who has some achievements in some competitions. If you are interested in, please feel free to contact me. From 220c173a2b76a6098a3d664e20d79093fc835cae Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 6 Jan 2018 17:06:48 -0600 Subject: [PATCH 28/80] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8700f08..4ca0646 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ author: Takanori MAEHARA (web: http://www.prefield.com, e-mail: maehara@prefield # Recruiting -I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute on artificial intelligence related areas. In particular, I am working on discrete algorithms (including topics in this github repository). We are hiring *strong programmers/researchers* who has some achievements in some competitions. If you are interested in, please feel free to contact me. +I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute. I am working on discrete algorithms (including topics in this github repository). We are hiring *strong programmers/researchers* who has some achievements in some competitions. If you are interested in, please feel free to contact me. From fb739fd03ce8675104a71328cb26bc87ddb2b94f Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 7 Jan 2018 10:57:41 +0900 Subject: [PATCH 29/80] Segment Arrangement (Bentley-Ottman's Plane-Sweep) --- geometry/segment_arrangement.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geometry/segment_arrangement.cc b/geometry/segment_arrangement.cc index 86d2212..ebdd923 100644 --- a/geometry/segment_arrangement.cc +++ b/geometry/segment_arrangement.cc @@ -8,7 +8,7 @@ // time, where k is the size of output. // // Complexity: -// O(k log n), where k is the size of input. +// O(k log n), where k is the size of output. // // Verified: // AOJ 1226 From e95eb8590abbb72bb1c98fca72a372ca599e75c4 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 7 Jan 2018 03:56:40 -0600 Subject: [PATCH 30/80] Prufer Code in O(n) --- graph/prufer_code.cc | 112 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 graph/prufer_code.cc diff --git a/graph/prufer_code.cc b/graph/prufer_code.cc new file mode 100644 index 0000000..8e4b877 --- /dev/null +++ b/graph/prufer_code.cc @@ -0,0 +1,112 @@ +// +// Prufer Code in Linear Time +// +// Description: +// Prufer code gives one-to-one correspondence +// between labeled trees and integers of length n-2. +// This immediately shows the Cayley theorem: +// the number of labeled trees is n^{n-2}. +// The algorithm computes the Prufer code in linear +// time. +// +// Complexity: +// O(n) +// +// Reference: +// Xiaodong Wang, Lei Wang, and Yingjie Wui (2009): +// "An Optimal Algorithm for Prufer Codes", +// Journal of Software Engineering and Applications, +// vol.2, 111--115. +// + +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +struct Tree { + int n; + vector> adj; + Tree(int n) : n(n), adj(n) { } + void addEdge(int u, int v) { + adj[u].push_back(v); + adj[v].push_back(u); + } +}; + +vector labeledTreeToCode(Tree T) { + vector deg(T.n), parent(T.n, -1), code; + function dfs = [&](int u) { + deg[u] = T.adj[u].size(); + for (int v: T.adj[u]) { + if (v != parent[u]) { + parent[v] = u; + dfs(v); + } + } + }; dfs(T.n-1); + + int index = -1; + while (deg[++index] != 1); + for (int u = index, i = 0; i < T.n-2; ++i) { + int v = parent[u]; + code.push_back(v); + if (--deg[v] == 1 && v < index) { + u = v; + } else { + while (deg[++index] != 1); + u = index; + } + } + return code; +} + +Tree codeToLabeledTree(vector code) { + int n = code.size() + 2; + Tree T(n); + vector deg(n, 1); + for (int i = 0; i < n-2; ++i) + ++deg[code[i]]; + + int index = -1; + while (deg[++index] != 1); + for (int u = index, i = 0; i < n-2; ++i) { + int v = code[i]; + T.addEdge(u, v); + --deg[u]; --deg[v]; + if (deg[v] == 1 && v < index) { + u = v; + } else { + while (deg[++index] != 1); + u = index; + } + } + for (int u = 0; u < n-1; ++u) + if (deg[u] == 1) T.addEdge(u, n-1); + return T; +} + +int main() { + Tree T(6); + T.addEdge(0, 3); + T.addEdge(1, 3); + T.addEdge(2, 3); + T.addEdge(3, 4); + T.addEdge(4, 5); + auto code = labeledTreeToCode(T); + for (int u: code) { + cout << u << " "; + } + cout << endl; + + Tree G = codeToLabeledTree(code); + for (int u = 0; u < G.adj.size(); ++u) { + for (int v: G.adj[u]) { + if (u < v) cout << u << " " << v << endl; + } + } +} From bcdecdd6191c054a3583fcec845efa2ee4bc3f8f Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 7 Jan 2018 04:01:37 -0600 Subject: [PATCH 31/80] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ca0646..b51f54d 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ author: Takanori MAEHARA (web: http://www.prefield.com, e-mail: maehara@prefield # Recruiting -I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute. I am working on discrete algorithms (including topics in this github repository). We are hiring *strong programmers/researchers* who has some achievements in some competitions. If you are interested in, please feel free to contact me. +I am a researcher at *RIKEN Center for Advanced Intelligence Project*, which is a Japanese governmental academic research institute. I am working on discrete algorithms (including topics in this github repository). We are hiring *strong programmers* who has some achievements in some competitions. If you are interested in, please feel free to contact me. From c64100bd11a658744cecf681c1fbdc8cb334dc05 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Mon, 8 Jan 2018 15:41:45 -0600 Subject: [PATCH 32/80] Bounded Knapsack Problem in O(nW) time --- dynamic_programming/bounded_knapsack.cc | 134 ++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 dynamic_programming/bounded_knapsack.cc diff --git a/dynamic_programming/bounded_knapsack.cc b/dynamic_programming/bounded_knapsack.cc new file mode 100644 index 0000000..9dc1ce9 --- /dev/null +++ b/dynamic_programming/bounded_knapsack.cc @@ -0,0 +1,134 @@ +// +// Bounded Knapsack Problem +// +// Description: +// +// There are n kinds of items of profit pi, weight bi, and +// amount mi (i in {0...n}). For a given W, we want to select items +// to maximize the total profit subject to the total weight +// is at most W and the number of each item is at most mi. +// +// This problem can be solved by the following DP. +// E[j][w] = max {E[j-1][w], E[j-1][w-bj]+pj, E[j-1][w-2bj]+2pj. ...} +// A naive implementation requires O(nmW) time. However, we can reduce +// this to O(nW) as follows. +// +// We compute E[j][s], E[j][s+bj]. E[s+2bj] ... for each s in [0,bj). +// For simplicity, we consider s = 0. Then, we have +// E[j][w+bj] = max {E[j-1][w+bj], E[j-1][w]+pj, E[j-1][w-bj]+2pj. ...} +// By comparing this with the original formula, +// - E[j][w+bj] contains E[j-1][w+bj] term +// - E[j][w+bj] does not contain E[j-1][w-mjbj] term +// - The all terms have been added pj +// Thus, by using a data structure that supports these operations, +// we can perform the DP efficiently. The data structure is implemented +// by a sliding maximum queue with one additional integer parameter. +// +// Complexity: +// +// O(n W) +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +// Minimum Queue or Sliding Minimum Data Dtructure +// +// 3 1 4 1 +// 1 4 1 +template +struct MinQueue { + queue que; + deque peek; + T alpha = 0; + void push(T a) { + a -= alpha; + que.push(a); + while (!peek.empty() && peek.back() > a) peek.pop_back(); + peek.push_back(a); + } + void pop() { + if (que.front() == peek.front()) peek.pop_front(); + que.pop(); + } + T front() const { return que.front()+alpha; } + T min() const { return peek.front()+alpha; } + void add(T a) { alpha += a; } // does not change the ordering +}; + +int boundedKnapsackDP(vector ps, + vector ws, + vector ms, + int W) { + int n = ps.size(); + vector> dp(n+1, vector(W+1)); + for (int i = 0; i < n; ++i) { + for (int s = 0; s < ws[i]; ++s) { + int alpha = 0; + queue que; + deque peek; + for (int w = s; w <= W; w += ws[i]) { + alpha += ps[i]; + int a = dp[i][w]-alpha; + que.push(a); + while (!peek.empty() && peek.back() < a) peek.pop_back(); + peek.push_back(a); + while (que.size() > ms[i]+1) { + if (que.front() == peek.front()) peek.pop_front(); + que.pop(); + } + dp[i+1][w] = peek.front()+alpha; + } + } + } + int ans = 0; + for (int w = 0; w <= W; ++w) + ans = max(ans, dp[n][w]); + return ans; +} + +int boundedKnapsackDPNaive(vector ps, + vector ws, + vector ms, + int W) { + int n = ps.size(); + vector> dp(n+1, vector(W+1)); + for (int i = 0; i < n; ++i) { + for (int w = 0; w <= W; ++w) { + dp[i+1][w] = dp[i][w]; + for (int j = 1; j <= ms[i]; ++j) { + if (w - j*ws[i] < 0) break; + dp[i+1][w] = max(dp[i+1][w], dp[i][w-j*ws[i]]+j*ps[i]); + } + } + } + int ans = 0; + for (int w = 0; w <= W; ++w) + ans = max(ans, dp[n][w]); + return ans; +} + +int main() { + int seed; + seed = 20; + //cin >> seed; + seed = time(0); + srand(seed); + int n = 100; + int W = 10000; + vector ps(n), ws(n), ms(n); + for (int i = 0; i < n; ++i) { + ps[i] = rand() % n + 1; + ws[i] = rand() % n + 1; + ms[i] = rand() % n + 1; + //cout << ps[i] << " " << ws[i] << " " << ms[i] << endl; + } + + cout << boundedKnapsackDP(ps, ws, ms, W) << endl; + cout << boundedKnapsackDPNaive(ps, ws, ms, W) << endl; +} From 2bcb86f2751548841d77c0db1db3e32adf99b5dd Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Mon, 8 Jan 2018 15:43:48 -0600 Subject: [PATCH 33/80] Bounded Knapsack Problem in O(nW) time --- dynamic_programming/bounded_knapsack.cc | 26 +------------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/dynamic_programming/bounded_knapsack.cc b/dynamic_programming/bounded_knapsack.cc index 9dc1ce9..84b1402 100644 --- a/dynamic_programming/bounded_knapsack.cc +++ b/dynamic_programming/bounded_knapsack.cc @@ -22,7 +22,7 @@ // - The all terms have been added pj // Thus, by using a data structure that supports these operations, // we can perform the DP efficiently. The data structure is implemented -// by a sliding maximum queue with one additional integer parameter. +// by a maximum queue with one accumulation parameter. // // Complexity: // @@ -37,30 +37,6 @@ using namespace std; #define all(c) ((c).begin()), ((c).end()) #define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } -// Minimum Queue or Sliding Minimum Data Dtructure -// -// 3 1 4 1 -// 1 4 1 -template -struct MinQueue { - queue que; - deque peek; - T alpha = 0; - void push(T a) { - a -= alpha; - que.push(a); - while (!peek.empty() && peek.back() > a) peek.pop_back(); - peek.push_back(a); - } - void pop() { - if (que.front() == peek.front()) peek.pop_front(); - que.pop(); - } - T front() const { return que.front()+alpha; } - T min() const { return peek.front()+alpha; } - void add(T a) { alpha += a; } // does not change the ordering -}; - int boundedKnapsackDP(vector ps, vector ws, vector ms, From 47c1996ce0cd69f6c2c441674968d9e4839c73e9 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 9 Jan 2018 20:28:04 +0900 Subject: [PATCH 34/80] Kd-Tree --- geometry/kd_tree.cc | 180 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 geometry/kd_tree.cc diff --git a/geometry/kd_tree.cc b/geometry/kd_tree.cc new file mode 100644 index 0000000..48f2df2 --- /dev/null +++ b/geometry/kd_tree.cc @@ -0,0 +1,180 @@ +// +// Kd-Tree +// +// Description: +// +// Kd-Tree is a spatial partitioning tree defined as follows[1]. +// Each node has a point, called a pivot, and its left +// and right childs store the points that are lefter and righter +// on the pivot. Here, "left" and "right" are defined by the +// coordinate that switches cyclically over the depth. +// +// The Kd-Tree has the depth O(log n), and if the input is random, +// the searching has complexity O(log n) in expectation. +// However, in an adversarial input, the worst case complexity +// becomes O(n^{1-1/d}), which tends to a naive bound as d to infty[2]. +// +// Note: Computing NNs for n points requires O(n^{2-1/d}) time. +// Basically, it is practical and has advantage over the naive search +// if n = 10^5. +// +// Complexity: +// +// Construction: O(n log n) +// Nearest Neighbor: O(n^{1-1/d}) in worst case, +// O(log n) in random case. +// +// References: +// +// [1] Jon Louis Bentley (1975): +// "Multidimensional binary search trees used for associative searching." +// Communications of the ACM, vol. 18, no. 9, pp. 509--517. +// +// [2] Der-Tsai Lee and C. K. Wong (1977): +// "Worst-case analysis for region and partial region searches in +// multidimensional binary search trees and balanced quad trees." +// Acta Informatica, vol.9, no.1, pp. 23--29. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} + +using Real = long double; +const Real PI = acos(-1.0); +const Real EPS = 1e-8; +int sign(Real x) { return (x > EPS) - (x < -EPS); } +struct Point { + Real x, y; + Point &operator+=(Point p) { x += p.x; y += p.y; return *this; } + Point &operator*=(Real a) { x *= a; y *= a; return *this; } + Point operator+() const { return {+x, +y}; } + Point operator-() const { return {-x, -y}; } + + Point &operator-=(Point p) { return *this += -p; } + Point &operator/=(Real a) { return *this *= 1/a; } +}; +Point operator+(Point p, Point q) { return p += q; } +Point operator-(Point p, Point q) { return p -= q; } +Point operator*(Real a, Point p) { return p *= a; } +Point operator*(Point p, Real a) { return p *= a; } +Point operator/(Point p, Real a) { return p /= a; } + +int compare(Point p, Point q) { + int s = sign(p.x - q.x); + return s ? s : sign(p.y - q.y); +} +bool operator==(Point p, Point q) { return compare(p,q)==0; } +bool operator!=(Point p, Point q) { return compare(p,q)!=0; } +bool operator<=(Point p, Point q) { return compare(p,q)<=0; } +bool operator>=(Point p, Point q) { return compare(p,q)>=0; } +bool operator<(Point p, Point q) { return compare(p,q)<0; } +bool operator>(Point p, Point q) { return compare(p,q)>0; } + +Real dot(Point p, Point q) { return p.x*q.x+p.y*q.y; } +Real cross(Point p, Point q) { return p.x*q.y-p.y*q.x; } // left turn > 0 +Real norm2(Point p) { return dot(p,p); } +Point orth(Point p) { return {-p.y, p.x}; } +Real norm(Point p) { return sqrt(dot(p,p)); } +Real arg(Point p) { return atan2(p.y, p.x); } +Real arg(Point p, Point q){ return atan2(cross(p,q), dot(p,q)); } + +istream &operator>>(istream &is, Point &p) { is>>p.x>>p.y;return is; } +ostream &operator<<(ostream &os, const Point &p) { os<<"("< ps; + KdTree(vector ps) : ps(ps) { + int idx[ps.size()]; + iota(idx, idx+ps.size(), 0); + root = build<0>(idx, idx+ps.size()); + } + template + Node *build(int *l, int *r) { + if (l - r >= 0) return 0; + auto comp = [&](int i, int j) { + if (d == 0) return ps[i].x < ps[j].x; + if (d == 1) return ps[i].y < ps[j].y; + }; + int *m = l + (r-l)/2; + nth_element(l, m, r, comp); + return new Node({*m, build(l,m), build(m+1,r)}); + } + template + void nearestNeighborSearchRec(Node *x, Point p, pair &ub) { + if (!x) return; + Point q = p - ps[x->id]; + Real r = norm(q), w; + if (r < ub.fst) ub = {r, x->id}; + if (d == 0) w = q.x; + else w = q.y; + Node *fst = x->left, *snd = x->right; + if (w > 0) swap(fst, snd); + nearestNeighborSearchRec(fst, p, ub); + if (ub.fst > abs(w)) nearestNeighborSearchRec(snd, p, ub); + } + int nearestNeighbor(Point p) { + pair ub(1.0/0.0, -1); + nearestNeighborSearchRec<0>(root, p, ub); + return ub.snd; + } + + + // for verification + int nearestNeighborNaive(Point p) { + int id = 0; + for (int i = 1; i < ps.size(); ++i) + if (norm(ps[i] - p) < norm(ps[id] - p)) id = i; + return id; + } + void display(Node *x, int tab=0) { + if (!x) return; + display(x->left, tab+2); + cout << string(tab, ' ') << x->id << ": " << ps[x->id] << endl; + display(x->right,tab+2); + } +}; +int main() { + int n = 10000; + vector ps(n); + for (int i = 0; i < n; ++i) { + ps[i].x = rand() % n; + ps[i].y = rand() % n; + } + KdTree T(ps); + //T.display(T.root); + + double t1 = 0, t2 = 0; + for (int k = 0; k < n; ++k) { + Point p = {rand() % n, rand() % n}; + tick(); + Point q = ps[T.nearestNeighbor(p)]; + t1 += tick(); + Point r = ps[T.nearestNeighborNaive(p)]; + t2 += tick(); + TEST(sign(norm(q-p) - norm(r-p)) == 0); + //cout << p << " " << norm(q - p) << " " << norm(r - p) << endl; + } + cout << t1 << " [s] by Kd search" << endl << + t2 << " [s] by naive search" << endl; +} From 2f7430f0139f7d03d9f5f9a2bdc8a697bc69ca19 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 9 Jan 2018 06:02:53 -0600 Subject: [PATCH 35/80] Bounded Knapsack Problem in O(nW) time --- dynamic_programming/bounded_knapsack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamic_programming/bounded_knapsack.cc b/dynamic_programming/bounded_knapsack.cc index 84b1402..0bff66f 100644 --- a/dynamic_programming/bounded_knapsack.cc +++ b/dynamic_programming/bounded_knapsack.cc @@ -4,7 +4,7 @@ // Description: // // There are n kinds of items of profit pi, weight bi, and -// amount mi (i in {0...n}). For a given W, we want to select items +// amount mi (i in {0...n-1}). For a given W, we want to select items // to maximize the total profit subject to the total weight // is at most W and the number of each item is at most mi. // From 2aae310a170e5283ddf026fc81734770f48fe06f Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Thu, 11 Jan 2018 15:50:27 +0900 Subject: [PATCH 36/80] Random Ball Cover --- geometry/random_ball_cover.cc | 225 ++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 geometry/random_ball_cover.cc diff --git a/geometry/random_ball_cover.cc b/geometry/random_ball_cover.cc new file mode 100644 index 0000000..f2266b2 --- /dev/null +++ b/geometry/random_ball_cover.cc @@ -0,0 +1,225 @@ +// +// Random Ball Cover +// +// Description: +// +// Random ball cover is a data structure for metric nearest neighbor +// search. It is useful if the only distance function is available, +// and/or dimension is moderately large (10 to 1000). +// +// It first select O(sqrt{n})-size random sample from the points as +// representatives. Then, it assigns other points to the nearest +// representatives. The search procedure uses the triangle inequality +// to prune unnecessary searching. +// +// +// Complexity: +// +// Construction: O(n log n). +// Search: O(sqrt(n)) in a random distribution. +// +// +// Reference: +// +// Lawrence Cayton (2012): +// "Accelerating nearest neighbor search on manycore systems." +// Parallel & Distributed Processing Symposium (IPDPS), +// in IPDPS, pp.402-413. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} + + +using Real = long double; +const Real PI = acos(-1.0); +const Real EPS = 1e-8; +int sign(Real x) { return (x > EPS) - (x < -EPS); } +struct Point { + Real x, y; + Point &operator+=(Point p) { x += p.x; y += p.y; return *this; } + Point &operator*=(Real a) { x *= a; y *= a; return *this; } + Point operator+() const { return {+x, +y}; } + Point operator-() const { return {-x, -y}; } + + Point &operator-=(Point p) { return *this += -p; } + Point &operator/=(Real a) { return *this *= 1/a; } +}; +Point operator+(Point p, Point q) { return p += q; } +Point operator-(Point p, Point q) { return p -= q; } +Point operator*(Real a, Point p) { return p *= a; } +Point operator*(Point p, Real a) { return p *= a; } +Point operator/(Point p, Real a) { return p /= a; } + +int compare(Point p, Point q) { + int s = sign(p.x - q.x); + return s ? s : sign(p.y - q.y); +} +bool operator==(Point p, Point q) { return compare(p,q)==0; } +bool operator!=(Point p, Point q) { return compare(p,q)!=0; } +bool operator<=(Point p, Point q) { return compare(p,q)<=0; } +bool operator>=(Point p, Point q) { return compare(p,q)>=0; } +bool operator<(Point p, Point q) { return compare(p,q)<0; } +bool operator>(Point p, Point q) { return compare(p,q)>0; } + +Real dot(Point p, Point q) { return p.x*q.x+p.y*q.y; } +Real cross(Point p, Point q) { return p.x*q.y-p.y*q.x; } // left turn > 0 +Real norm2(Point p) { return dot(p,p); } +Point orth(Point p) { return {-p.y, p.x}; } +Real norm(Point p) { return sqrt(dot(p,p)); } +Real arg(Point p) { return atan2(p.y, p.x); } +Real arg(Point p, Point q){ return atan2(cross(p,q), dot(p,q)); } + +istream &operator>>(istream &is, Point &p) { is>>p.x>>p.y;return is; } +ostream &operator<<(ostream &os, const Point &p) { os<<"("< ps; + vector>> list; + RandomBallCover(vector ps) : ps(ps) { + int n = ps.size(), sqrtn = sqrt(n + 0.5); + vector idx(n); + iota(all(idx), 0); + random_shuffle(all(idx)); + for (int i = 0; i < sqrtn; ++i) + list.push_back({make_pair(0.0, idx[i])}); + for (int i = sqrtn; i < n; ++i) { + Real nearest = 1.0/0.0; + int id; + for (int j = 0; j < sqrtn; ++j) { + Real d = dist(ps[list[j][0].snd], ps[idx[i]]); + if (nearest > d) { + nearest = d; + id = j; + } + } + list[id].push_back({nearest, idx[i]}); + } + for (int i = 0; i < list.size(); ++i) + sort(all(list[i])); + } + int nearestNeighbor(Point p) { + vector dis(list.size()), rem(list.size()); + vector cand(list.size()); + Real best = 1.0/0.0; + int id; + for (int i = 0; i < list.size(); ++i) { + dis[i] = dist(p, ps[list[i][0].snd]); + if (dis[i] < best) { + best = dis[i]; + id = list[i][0].snd; + } + cand[i] = i; + } + sort(all(cand), [&](int i, int j) { return dis[i] < dis[j]; }); + for (int i: cand) { + for (int k = list[i].size()-1; k >= 0; --k) { + if (best <= dis[i] - list[i][k].fst) break; + int j = list[i][k].snd; + Real d = dist(p, ps[j]); + if (d < best) { + best = d; + id = j; + } + } + } + return id; + } + int nearestNeighborNaive(Point p) { + int id = 0; + for (int i = 0; i < ps.size(); ++i) + if (dist(p, ps[id]) > dist(p, ps[i])) id = i; + return id; + } +}; + + +// for comparison +// Kd-Tree for k = 2 +struct KdTree { + struct Node { + int id; + Node *left, *right; + } *root = 0; + vector ps; + KdTree(vector ps) : ps(ps) { + int idx[ps.size()]; + iota(idx, idx+ps.size(), 0); + root = build<0>(idx, idx+ps.size()); + } + template + Node *build(int *l, int *r) { + if (l - r >= 0) return 0; + auto comp = [&](int i, int j) { + if (d == 0) return ps[i].x < ps[j].x; + if (d == 1) return ps[i].y < ps[j].y; + }; + int *m = l + (r-l)/2; + nth_element(l, m, r, comp); + return new Node({*m, build(l,m), build(m+1,r)}); + } + template + void nearestNeighborSearchRec(Node *x, Point p, pair &ub) { + if (!x) return; + Point q = p - ps[x->id]; + Real r = norm(q), w; + if (r < ub.fst) ub = {r, x->id}; + if (d == 0) w = q.x; + else w = q.y; + Node *fst = x->left, *snd = x->right; + if (w > 0) swap(fst, snd); + nearestNeighborSearchRec(fst, p, ub); + if (ub.fst > abs(w)) nearestNeighborSearchRec(snd, p, ub); + } + int nearestNeighbor(Point p) { + pair ub(1.0/0.0, -1); + nearestNeighborSearchRec<0>(root, p, ub); + return ub.snd; + } +}; + +int main() { + int n = 10000; + vector ps; + for (int i = 0; i < n; ++i) { + Real x = rand() % n; + Real y = rand() % n; + ps.push_back({x, y}); + } + RandomBallCover X(ps); + KdTree T(ps); + double t1 = 0, t2 = 0; + for (int i = 0; i < n; ++i) { + Real x = rand() % n; + Real y = rand() % n; + Point p = {x, y}; + tick(); + int j = X.nearestNeighbor(p); + t1 += tick(); + int k = T.nearestNeighbor(p); + t2 += tick(); + assert(sign(norm(p - ps[j]) - norm(p - ps[k])) == 0); + } + cout << t1 << " " << t2 << endl; +} From ee567e83da9dc714ccac3566e71554e457a2e6cd Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 13 Jan 2018 20:46:58 +0900 Subject: [PATCH 37/80] Hopcroft's DA Minimization --- string/dfa_minimization.cc | 295 +++++++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 string/dfa_minimization.cc diff --git a/string/dfa_minimization.cc b/string/dfa_minimization.cc new file mode 100644 index 0000000..6628589 --- /dev/null +++ b/string/dfa_minimization.cc @@ -0,0 +1,295 @@ +// +// Deterministic Finite Automata Minimization +// +// Description: +// +// Hopcroft minimization algorithm. See Wikipedia. +// +// Complexity: +// +// O(|A| n log n). +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +struct Automaton { + vector> trans; + vector is_accept; + int init = 0; + int next(int state, int a) { return trans[state][a]; } + bool accept(int state) { return is_accept[state]; } + int size() { return trans.size(); } +}; +template +Automaton minimizeAutomaton(AM A) { + // remove unreachables + vector seen(A.size()); + seen[A.init] = 1; + vector partition = {A.init}, pos, label; + for (int i = 0; i < partition.size(); ++i) { + pos.push_back(i); + label.push_back(0); + int state = partition[i]; + for (int a = 0; a <= 9; ++a) { + int state_ = A.next(state, a); + if (!seen[state_]) { + seen[state_] = 1; + partition.push_back(state_); + } + } + } + // make inverse mapping + vector inverse[partition.size()][10]; + for (int i = 0; i < partition.size(); ++i) { + int state = partition[i]; + for (int a = 0; a <= 9; ++a) { + inverse[A.next(state, a)][a].push_back(state); + } + } + // Hopcroft minimization + vector begin = {0}, mid = {0}, end = {partition.size()}; + auto mark = [&](int state) { + int x = label[state]; + if (pos[state] < mid[x]) return; + int state_ = partition[mid[x]++]; + swap(pos[state], pos[state_]); + swap(partition[pos[state]], partition[pos[state_]]); + }; + auto refine = [&](int x) { + if (mid[x] == begin[x]) return -1; + if (mid[x] == end[x]) { mid[x] = begin[x]; return -1; } + int y = begin.size(); + if (mid[x] - begin[x] < end[x] - mid[x]) { + begin.push_back(begin[x]); + end.push_back(mid[x]); + mid.push_back(begin[x]); + begin[x] = mid[x]; + } else { + begin.push_back(mid[x]); + end.push_back(end[x]); + mid.push_back(mid[x]); + end[x] = mid[x]; + mid[x] = begin[x]; + } + for (int i = begin.back(); i < end.back(); ++i) + label[partition[i]] = y; + return y; + }; + for (int state: partition) + if (A.accept(state)) mark(state); + + if (refine(0) >= 0) { // do Hopcroft minimization + fill(all(seen), 0); + seen[0] = seen[1] = 1; + vector process = {0, 1}; + vector is_suspect(partition.size()); + while (!process.empty()) { + int x = process.back(); process.pop_back(); + seen[x] = 0; + for (int a = 0; a <= 9; ++a) { + vector suspect; + for (int i = begin[x]; i < end[x]; ++i) { + int u = partition[i]; + for (int state: inverse[u][a]) { + int y = label[state]; + mark(state); + if (!is_suspect[y]) { + is_suspect[y] = 1; + suspect.push_back(y); + } + } + } + for (int y: suspect) { + is_suspect[y] = 0; + int z = refine(y); + if (z < 0) continue; + if (seen[y]) { + process.push_back(z); + seen[z] = 1; + } else { + if (end[y] - begin[y] < end[z] - begin[z]) { + process.push_back(y); + seen[y] = 1; + } else { + process.push_back(z); + seen[z] = 1; + } + } + } + } + } + } + Automaton M; // completion + M.trans.assign(begin.size(), vector(10)); + M.is_accept.resize(begin.size()); + for (int x = 0; x < begin.size(); ++x) { + int state = partition[begin[x]]; + M.is_accept[x] = A.accept(state); + for (int a = 0; a <= 9; ++a) { + int y = label[A.next(state, a)]; + M.trans[x][a] = label[A.next(state, a)]; + } + } + M.init = label[A.init]; + return M; +} + + +// state = x : x == n % mod +struct ModuloAutomaton { + int mod; + ModuloAutomaton(int mod) : mod(mod) { } + int init = 0; + int size() { return mod; } + int next(int state, int a) { return (10 * state + a) % mod; } + bool accept(int state) { return state == 0; } +}; +// state = 0 : empty +// 1 : fail +// 2 ... 10 : singleton and last number is state-1 +// 11 ... 19 : increased and last number is state-10 +// 20 ... 28 : decreased and last number is state-20 +struct ZigZagAutomaton { + int init = 0; + int size() { return 29; } + int next(int state, int a) { + if (state == 0) return a == 0 ? 0 : a + 1; + if (state == 1) return 1; + if (state <= 10) { + int last = state - 1; + if (a > last) return a + 10; + else if (a < last) return a + 20; + } else if (state <= 19) { + int last = state - 10; + if (a < last) return a + 20; + } else if (state <= 28) { + int last = state - 20; + if (a > last) return a + 10; + } + return 1; + } + bool accept(int state) { return state != 1; } +}; +template +Automaton intersectionAutomaton(Automaton1 A, Automaton2 B) { + Automaton M; + vector> table(A.size(), vector(B.size(), -1)); + vector x = {A.init}, y = {B.init}; + table[x[0]][y[0]] = 0; + for (int i = 0; i < x.size(); ++i) { + M.trans.push_back(vector(10, -1)); + M.is_accept.push_back(A.accept(x[i]) && B.accept(y[i])); + for (int a = 0; a <= 9; ++a) { + int u = A.next(x[i], a), v = B.next(y[i], a); + if (table[u][v] == -1) { + table[u][v] = x.size(); + x.push_back(u); + y.push_back(v); + } + M.trans[i][a] = table[u][v]; + } + } + return M; +} + +template +int digitDP(string num, Automaton A, int eq = 1) { + int n = num.size(); + vector>> dp(n+1); + + dp[0] = vector>(2, vector(A.size())); + dp[0][1][A.init] = 1; + auto addTo = [&](int &x, int y) { + if ((x += y) >= 10000) x -= 10000; + }; + for (int i = 0; i < n; ++i) { + dp[i+1] = vector>(2, vector(A.size())); + for (int tight = 0; tight <= 1; ++tight) { + for (int state = 0; state < A.size(); ++state) { + if (dp[i][tight][state] == 0) continue; + int lim = (tight ? num[i] - '0' : 9); + for (int d = 0; d <= lim; ++d) { + int tight_ = tight && d == lim; + int state_ = A.next(state, d); + addTo(dp[i+1][tight_][state_], dp[i][tight][state]); + } + } + } + dp[i].clear(); + } + int ans = 0; + for (int tight = 0; tight <= eq; ++tight) + for (int state = 0; state < A.size(); ++state) + if (A.accept(state)) addTo(ans, dp[n][tight][state]); + return ans; +} + +template +int debug(string num, Automaton A) { + function rec + = [&](int i, int tight, int state, string s) { + if (i == num.size()) { + if (A.accept(state)) cout << s << endl; + return; + } + int lim = (tight ? num[i] - '0' : 9); + for (int d = 0; d <= lim; ++d) { + int tight_ = tight && d == lim; + int state_ = A.next(state, d); + s.push_back('0' + d); + rec(i+1, tight_, state_, s); + s.pop_back(); + } + }; + rec(0, 1, A.init, ""); +} + +void AOJ_ZIGZAG() { + char A[1000], B[1000]; + int M; + scanf("%s %s %d", A, B, &M); + ZigZagAutomaton zigzag; + ModuloAutomaton modulo(M); + auto IM = minimizeAutomaton(intersectionAutomaton(zigzag, modulo)); + int a = digitDP(A, IM, 0); + int b = digitDP(B, IM, 1); + cout << (b + (10000 - a)) % 10000 << endl; + { + auto IM = intersectionAutomaton(zigzag, modulo); + int a = digitDP(A, IM, 0); + int b = digitDP(B, IM, 1); + cout << (b + (10000 - a)) % 10000 << endl; + } +} +int main() { + AOJ_ZIGZAG(); + /* + Automaton A; + A.trans.assign(7, vector(10, 6)); + A.trans[0][0] = 1; + A.trans[0][1] = 2; + A.trans[1][0] = 0; + A.trans[1][1] = 3; + A.trans[2][0] = 4; + A.trans[2][1] = 5; + A.trans[3][0] = 4; + A.trans[3][1] = 5; + A.trans[4][0] = 4; + A.trans[4][1] = 5; + A.trans[5][0] = 5; + A.trans[5][1] = 5; + A.is_accept.assign(7, 0); + A.is_accept[2] = 1; + A.is_accept[3] = 1; + A.is_accept[4] = 1; + auto B = minimizeAutomaton(A); + cout << B.size() << endl; + */ + //minimizeAutomaton(ZigZagAutomaton()); +} From 7af336c54a9df8ee8d57d6c553cf8b3ca93141f6 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 13 Jan 2018 20:47:39 +0900 Subject: [PATCH 38/80] Hopcroft's DFA Minimization --- string/dfa_minimization.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/string/dfa_minimization.cc b/string/dfa_minimization.cc index 6628589..731338a 100644 --- a/string/dfa_minimization.cc +++ b/string/dfa_minimization.cc @@ -260,12 +260,6 @@ void AOJ_ZIGZAG() { int a = digitDP(A, IM, 0); int b = digitDP(B, IM, 1); cout << (b + (10000 - a)) % 10000 << endl; - { - auto IM = intersectionAutomaton(zigzag, modulo); - int a = digitDP(A, IM, 0); - int b = digitDP(B, IM, 1); - cout << (b + (10000 - a)) % 10000 << endl; - } } int main() { AOJ_ZIGZAG(); From f91a0c45ce42fc36bb13318b11f7c78d947f82f7 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 13 Jan 2018 21:37:35 +0900 Subject: [PATCH 39/80] Digit DP (most generalized form) --- dynamic_programming/digit_dp.cc | 225 ++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 dynamic_programming/digit_dp.cc diff --git a/dynamic_programming/digit_dp.cc b/dynamic_programming/digit_dp.cc new file mode 100644 index 0000000..4b72a97 --- /dev/null +++ b/dynamic_programming/digit_dp.cc @@ -0,0 +1,225 @@ +// +// Digit DP +// +// Description: +// +// Digit DP is a framework to solve problems of counting +// the numbers less than equal to a given number and +// whose digits satisfy some constraint. +// +// More generally, it can compute the sum-product +// sum { prod(x) : 0 <= x <= z } +// where +// prod(x) = (((e * x[0]) * x[1])...) * x[n-1]. +// +// The sum operator + is required to be commutative, and +// right-distributive with respect to * as +// (u + v) * d = (u * d + v * d) +// +// The constraint of digits should be represented by a +// finite automaton that reads digits from left to right. +// The DP has the table +// dp[digit][tight][status] +// with the following DP +// +// dp[0][1][M.init] = e +// for (int i = 0; i < n; ++i) { +// for (int tight = 0; tight <= 1; ++tight) { +// for (int state = 0; state < M.size(); ++state) { +// int lim = tight ? 'z'-0 : 9; +// for (int d = 0; d <= lim; ++d) { +// oplusTo(dp[i+1][tight&&d==lim][next(state,d)], +// otimes(dp[i][tight][state], d)); +// } +// } +// } +// } +// for (int tight = 0; tight <= 1; ++tight) +// for (int state = 0; state < M.size(); ++state) +// if (M.accept(state)) oplusTo(ans, dp[n][tight][state]; +// return ans; +// +// Verified: +// SPOJ_CPCRC1C +// AOJ_ZIGZAG + +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + + +// struct Value { +// Value &operator+(Value y) +// Value &operator*(int d) +// }; +// struct Automaton { +// int init +// int size() +// int next(int state, int d) +// bool accept(int state) +// }; +template +Value digitDP(string z, Value e, Automaton M, bool eq = 1) { + struct Maybe { + Value value; + bool undefined = true; + }; + auto oplusTo = [&](Maybe &x, Maybe y) { + if (x.undefined) x = y; + else if (!y.undefined) x.value += y.value; + }; + auto otimes = [&](Maybe x, int d) { + x.value *= d; + return x; + }; + int n = z.size(); + vector> curr(2, vector(M.size())); + curr[1][M.init] = {e, false}; + for (int i = 0; i < n; ++i) { + vector> next(2, vector(M.size())); + for (int tight = 0; tight <= 1; ++tight) { + for (int state = 0; state < M.size(); ++state) { + if (curr[tight][state].undefined) continue; + int lim = (tight ? z[i] - '0' : 9); + for (int d = 0; d <= lim; ++d) { + int tight_ = tight && d == lim; + int state_ = M.next(state, d); + oplusTo(next[tight_][state_], otimes(curr[tight][state], d)); + } + } + } + curr = next; + } + Maybe ans; + for (int tight = 0; tight <= eq; ++tight) + for (int state = 0; state < M.size(); ++state) + if (M.accept(state)) oplusTo(ans, curr[tight][state]); + return ans.value; +} + +template +string toString(T x) { + stringstream ss; + ss << x; + return ss.str(); +} + +using Int = long long; +Int sumOfDigits(string z, bool eq = true) { + struct Value { + Int count, sum; + Value &operator+=(Value y) { count+=y.count; sum+=y.sum; return *this; } + Value &operator*=(int d) { sum+=count*d; return *this; } + }; + struct Automaton { + int init = 0; + int size() { return 1; } + int next(int s, int d) { return 0; } + int accept(int s) { return true; } + }; + return digitDP(z, (Value){1,0}, Automaton(), eq).sum; +} +void SPOJ_CPCRC1C() { + for (long long a, b; cin >> a >> b; ) { + if (a < 0 && b < 0) break; + cout << sumOfDigits(toString(b), true) + - sumOfDigits(toString(a), false) << endl; + } +} + +struct Automaton { + vector> trans; + vector is_accept; + int init = 0; + int next(int state, int a) { return trans[state][a]; } + bool accept(int state) { return is_accept[state]; } + int size() { return trans.size(); } +}; +template +Automaton intersectionAutomaton(Automaton1 A, Automaton2 B) { + Automaton M; + vector> table(A.size(), vector(B.size(), -1)); + vector x = {A.init}, y = {B.init}; + table[x[0]][y[0]] = 0; + for (int i = 0; i < x.size(); ++i) { + M.trans.push_back(vector(10, -1)); + M.is_accept.push_back(A.accept(x[i]) && B.accept(y[i])); + for (int a = 0; a <= 9; ++a) { + int u = A.next(x[i], a), v = B.next(y[i], a); + if (table[u][v] == -1) { + table[u][v] = x.size(); + x.push_back(u); + y.push_back(v); + } + M.trans[i][a] = table[u][v]; + } + } + return M; +} + +void AOJ_ZIGZAG() { + char A[1000], B[1000]; + int M; + scanf("%s %s %d", A, B, &M); + + struct Value { + int value = 0; + Value &operator+=(Value x) { + if ((value += x.value) >= 10000) value -= 10000; + return *this; + } + Value &operator*=(int d) { + return *this; + } + } e = (Value){1}; + + // state = 0 : empty + // 1 : fail + // 2 ... 10 : singleton and last number is state-1 + // 11 ... 19 : increased and last number is state-10 + // 20 ... 28 : decreased and last number is state-20 + struct ZigZagAutomaton { + int init = 0; + int size() { return 29; } + int next(int state, int a) { + if (state == 0) return a == 0 ? 0 : a + 1; + if (state == 1) return 1; + if (state <= 10) { + int last = state - 1; + if (a > last) return a + 10; + else if (a < last) return a + 20; + } else if (state <= 19) { + int last = state - 10; + if (a < last) return a + 20; + } else if (state <= 28) { + int last = state - 20; + if (a > last) return a + 10; + } + return 1; + } + bool accept(int state) { return state != 1; } + } zigzag; + + // state = x : x == n % mod + struct ModuloAutomaton { + int mod; + ModuloAutomaton(int mod) : mod(mod) { } + int init = 0; + int size() { return mod; } + int next(int state, int a) { return (10 * state + a) % mod; } + bool accept(int state) { return state == 0; } + } modulo(M); + + auto IM = intersectionAutomaton(zigzag, modulo); + int a = digitDP(A, e, IM, 0).value; + int b = digitDP(B, e, IM, 1).value; + cout << (b + (10000 - a)) % 10000 << endl; +} + +int main() { + //SPOJ_CPCRC1C(); + AOJ_ZIGZAG(); +} From 7e2755e65a7aa324154bcb62b68769ad1afa5d69 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 13 Jan 2018 23:40:13 +0900 Subject: [PATCH 40/80] Longest ZigZag Subsequence in O(n) --- .../longest_zigzag_sequence.cc | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 dynamic_programming/longest_zigzag_sequence.cc diff --git a/dynamic_programming/longest_zigzag_sequence.cc b/dynamic_programming/longest_zigzag_sequence.cc new file mode 100644 index 0000000..0f31ad9 --- /dev/null +++ b/dynamic_programming/longest_zigzag_sequence.cc @@ -0,0 +1,64 @@ +// +// Longest ZigZag Subsequence +// +// Description: +// +// A sequence xs is zigzag if x[i] < x[i+1], x[i+1] > x[i+2], for all i +// (initial direction can be arbitrary). The maximum length zigzag +// subsequence is computed in O(n) time by a greedy method. +// +// First, we contract contiguous same numbers. Then, the number of +// peaks corresponds to the longest zig-zag subsequence. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +template +int longestZigZagSubsequence(vector xs) { + int n = xs.size(), len = 1, prev = -1; + for (int i = 0, j; i < n; i = j) { + for (j = i+1; j < n && xs[i] == xs[j]; ++j); + if (j < n) { + int sign = (xs[i] < xs[j]); + if (prev != sign) ++len; + prev = sign; + } + } + return len; +} + +// DP for verification +template +int longestZigZagSubsequenceN(vector A) { + int n = A.size(); + vector> Z(n, vector(2)); + Z[0][0] = 1; + Z[0][1] = 1; + int best = 1; + for(int i = 1; i < n; i++){ + for(int j = i-1; j>= 0; j--){ + if(A[j] < A[i]) Z[i][0] = max(Z[j][1]+1, Z[i][0]); + if(A[j] > A[i]) Z[i][1] = max(Z[j][0]+1, Z[i][1]); + } + best = max(best, max(Z[i][0],Z[i][1])); + } + return best; +} + +int main() { + for (int seed = 0; seed < 10000; ++seed) { + srand(seed); + int n = 100; + vector a(n); + for (int i = 0; i < n; ++i) { + a[i] = rand() % n; + } + assert(longestZigZagSubsequence(a) == longestZigZagSubsequenceN(a)); + } +} From d454c58e1c4271d486798e84401b2c7b49da469d Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 13 Jan 2018 23:40:52 +0900 Subject: [PATCH 41/80] Longest ZigZag Subsequence in O(n) --- .../{longest_zigzag_sequence.cc => longest_zigzag_subsequence.cc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dynamic_programming/{longest_zigzag_sequence.cc => longest_zigzag_subsequence.cc} (100%) diff --git a/dynamic_programming/longest_zigzag_sequence.cc b/dynamic_programming/longest_zigzag_subsequence.cc similarity index 100% rename from dynamic_programming/longest_zigzag_sequence.cc rename to dynamic_programming/longest_zigzag_subsequence.cc From b305b6d53fb7cf654938e85870b14b9859fef98e Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 18:43:18 +0900 Subject: [PATCH 42/80] General DigitDP --- dynamic_programming/digit_dp.cc | 52 ++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/dynamic_programming/digit_dp.cc b/dynamic_programming/digit_dp.cc index 4b72a97..33bf5cd 100644 --- a/dynamic_programming/digit_dp.cc +++ b/dynamic_programming/digit_dp.cc @@ -107,6 +107,17 @@ string toString(T x) { return ss.str(); } +// +// Sum of digits. +// Since sum is not distributive, (u + v) + d != (u + d + v + d), +// we need to augment the number to contain the number of numbers. +// Let + and * be defined by +// (u,a) + (v,b) = (u+v,a+b), +// (u,a) * d = (u+a*d,a). +// Then, they are right-distributive as +// ((u,a) + (v,b)) * c = (u+v,a+b) * c = (u+v+ac+bc,a+b), +// (u,a) * c + (v,b) * c = (u+ac,a) + (v+bc,b) = (u+v+ac+bc,a+b). +// using Int = long long; Int sumOfDigits(string z, bool eq = true) { struct Value { @@ -160,6 +171,13 @@ Automaton intersectionAutomaton(Automaton1 A, Automaton2 B) { return M; } +// +// Count the zigzag numbers that is a multiple of M. +// Here, a number is zigzag if its digits are alternatively +// increasing and decreasing, like 14283415... +// Since there are multiple conditions, we use automaton +// composition to simplify the approach. +// void AOJ_ZIGZAG() { char A[1000], B[1000]; int M; @@ -219,7 +237,39 @@ void AOJ_ZIGZAG() { cout << (b + (10000 - a)) % 10000 << endl; } +// +// Count the numbers that does not contain 4 and 7 in each digit. +// +void ABC007D() { + string a, b; + cin >> a >> b; + + struct ForbiddenNumber { + int init = 0; + int size() { return 2; } + int next(int state, int a) { + if (state == 1) return 1; + if (a == 4 || a == 9) return 1; + return 0; + } + bool accept(int state) { return state == 1; } + }; + struct Counter { + long long value = 0; + Counter &operator+=(Counter x) { + value += x.value; + return *this; + } + Counter &operator*=(int d) { + return *this; + } + }; + cout << digitDP(b, (Counter){1}, ForbiddenNumber(), true).value + - digitDP(a, (Counter){1}, ForbiddenNumber(), false).value << endl; +} + int main() { + ABC007D(); //SPOJ_CPCRC1C(); - AOJ_ZIGZAG(); + //AOJ_ZIGZAG(); } From 0c1fc116a41295c6b919312dde43f0c85886cc56 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 18:46:34 +0900 Subject: [PATCH 43/80] General Digit DP --- dynamic_programming/digit_dp.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dynamic_programming/digit_dp.cc b/dynamic_programming/digit_dp.cc index 33bf5cd..ec003ed 100644 --- a/dynamic_programming/digit_dp.cc +++ b/dynamic_programming/digit_dp.cc @@ -38,10 +38,15 @@ // for (int state = 0; state < M.size(); ++state) // if (M.accept(state)) oplusTo(ans, dp[n][tight][state]; // return ans; +// +// Here, tight means the left digits are tight so that the current +// digit cannot run over 0 to 9, and state means the state of the +// automaton, which compresses the 10^len states to |Automaton| states. // // Verified: // SPOJ_CPCRC1C // AOJ_ZIGZAG +// ABC_007D #include using namespace std; From af9a22a92953e903c474e98e79107103adbd0a53 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:09:56 +0900 Subject: [PATCH 44/80] Dijkstra Single Source Shortest Path --- graph/dijkstra.cc | 317 ++++++++++++++++++++++++++++++++++++++ graph/shortest_path_st.cc | 104 ------------- 2 files changed, 317 insertions(+), 104 deletions(-) create mode 100644 graph/dijkstra.cc delete mode 100644 graph/shortest_path_st.cc diff --git a/graph/dijkstra.cc b/graph/dijkstra.cc new file mode 100644 index 0000000..dfa4510 --- /dev/null +++ b/graph/dijkstra.cc @@ -0,0 +1,317 @@ +// +// Dijkstra's Single Source Shortest Path +// +// Description: +// +// Dijkstra algorithm finds a single source shortest path on +// a nonnegative weighted graph. +// It implements two algorithms with two data structures. +// (1) standard dijkstra with standard heap +// (2) bidirectional dijkstra with standard heap +// (3) standard dijkstra with radix heap +// (4) bidirectional dijkstra with radix heap +// +// For a simple test (see the code), we observe that +// Binomial,Unidirectional 14.74[s] +// Binomial Bidirectional 0.52[s] +// Radix,Unidirectional 4.90[s] +// Radix,Bidirectional 0.31[s] +// +// Complexity: +// +// O(m log n) for binomial heap +// O(m) for radix heap +// +// Verify: +// +// SPOJ_SHPATH (bidirectional search) +// +// +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +template +struct WeightedGraph { + struct Edge { + int to; + Weight weight; + }; + int n; + vector> adj, rdj; + WeightedGraph(int n) : n(n), adj(n), rdj(n) { } + void addEdge(int u, int v, Weight w) { + adj[u].push_back({v, w}); + rdj[v].push_back({u, w}); // can be omitted for the standard dijkstra + } +}; +template +struct ShortestPath { + WeightedGraph g; + ShortestPath(WeightedGraph g) : g(g), dist(g.n), prev(g.n) { } + + vector dist; + vector prev; + void solve(int s) { + prev.assign(g.n,-1); + dist.assign(g.n,-1); dist[s] = 0; + + using Node = pair; + priority_queue, greater> que; + que.push({0,s}); + while (!que.empty()) { + auto d = que.top().fst; + auto u = que.top().snd; + que.pop(); + if (dist[u] < d) continue; + for (auto e: g.adj[u]) { + auto v = e.to; + auto w = e.weight; + if (dist[v] >= 0 && dist[v] <= dist[u]+w) continue; + dist[v] = dist[u] + w; + prev[v] = u; + que.push({dist[v], v}); + } + } + } + int solve(int s, int t) { + if (s == t) return dist[s] = 0; + fill(all(dist), -1); dist[s] = 0; + vector drev(g.n, -1); drev[t] = 0; + + using Node = pair; + priority_queue, greater> qs, qt; + qs.push({0,s}); qt.push({0,t}); + int mu = -1; + while (!qs.empty() && !qt.empty()) { + if (mu >= 0 && qs.top().fst + qt.top().fst >= mu) break; + if (qs.top().fst <= qt.top().fst) { + auto d = qs.top().fst; + auto u = qs.top().snd; + qs.pop(); + if (dist[u] > d) continue; + for (auto e: g.adj[u]) { + auto v = e.to; + auto w = e.weight; + if (dist[v] >= 0 && dist[v] <= dist[u] + w) continue; + dist[v] = dist[u] + w; + qs.push({dist[v], v}); + if (drev[v] >= 0) { + auto nu = dist[v] + drev[v]; + if (mu < 0 || mu > nu) mu = nu; + } + } + } else { + auto d = qt.top().fst; + auto u = qt.top().snd; + qt.pop(); + if (drev[u] > d) continue; + for (auto e: g.rdj[u]) { + auto v = e.to; + auto w = e.weight; + if (drev[v] >= 0 && drev[v] <= drev[u] + w) continue; + drev[v] = drev[u] + w; + qt.push({drev[v], v}); + if (dist[v] >= 0) { + auto nu = dist[v] + drev[v]; + if (mu < 0 || mu > nu) mu = nu; + } + } + } + } + return mu; + } +}; + + +// +// Dijkstra with Radix Heap +// +template +struct RadixHeap { + using uint = uint32_t; // fix bit size + static int bsr(uint a) { return a ? 31 - __builtin_clz(a) : -1; } + uint size, last; + vector> v[33]; + RadixHeap() : size(0), last(0) { } + + bool empty() const { return size == 0; } + void aux(pair p) { v[bsr(p.fst^last)+1].push_back(p); } + pair top() { + if (v[0].empty()) { + int i = 1; + while (v[i].empty()) ++i; + last = min_element(all(v[i]))->fst; + for (auto p: v[i]) aux(p); + v[i].clear(); + } + return v[0].back(); + } + void push(uint key, T value) { ++size; aux({key, value}); } + void pop() { --size; top(); v[0].pop_back(); } +}; +template <> +struct ShortestPath { + using Weight = int; + WeightedGraph g; + + vector dist; + vector prev; + ShortestPath(WeightedGraph g) : g(g), dist(g.n), prev(g.n) { } + + void solve(int s) { + fill(all(prev), -1); + fill(all(dist), -1); dist[s] = 0; + + RadixHeap que; + que.push(0,s); + while (!que.empty()) { + auto d = que.top().fst; + auto u = que.top().snd; + que.pop(); + if (dist[u] < d) continue; + for (auto e: g.adj[u]) { + auto v = e.to; + auto w = e.weight; + if (dist[v] >= 0 && dist[v] <= dist[u]+w) continue; + dist[v] = dist[u] + w; + prev[v] = u; + que.push(dist[v], v); + } + } + } + // Bidirectional Dijkstra + int solve(int s, int t) { + if (s == t) return dist[s] = 0; + + fill(all(dist), -1); dist[s] = 0; + vector drev(g.n, -1); drev[t] = 0; + RadixHeap qs, qt; + qs.push(0,s); qt.push(0,t); + int mu = -1; + while (!qs.empty() && !qt.empty()) { + if (mu >= 0 && qs.top().fst + qt.top().fst >= mu) break; + if (qs.top().fst <= qt.top().fst) { + auto d = qs.top().fst; + auto u = qs.top().snd; + qs.pop(); + if (dist[u] > d) continue; + for (auto e: g.adj[u]) { + auto v = e.to; + auto w = e.weight; + if (dist[v] >= 0 && dist[v] <= dist[u] + w) continue; + dist[v] = dist[u] + w; + qs.push(dist[v], v); + if (drev[v] >= 0) { + auto nu = dist[v] + drev[v]; + if (mu < 0 || mu > nu) mu = nu; + } + } + } else { + auto d = qt.top().fst; + auto u = qt.top().snd; + qt.pop(); + if (drev[u] > d) continue; + for (auto e: g.rdj[u]) { + auto v = e.to; + auto w = e.weight; + if (drev[v] >= 0 && drev[v] <= drev[u] + w) continue; + drev[v] = drev[u] + w; + qt.push(drev[v], v); + if (dist[v] >= 0) { + auto nu = dist[v] + drev[v]; + if (mu < 0 || mu > nu) mu = nu; + } + } + } + } + return mu; + } +}; + +void SPOJ_SHPATH() { + int ncase; scanf("%d", &ncase); + for (int icase = 0; icase < ncase; ++icase) { + int n; + scanf("%d", &n); + WeightedGraph g(n); + map id; + for (int u = 0; u < n; ++u) { + char name[1024]; + scanf("%s", name); + id[name] = u; + int k; + scanf("%d", &k); + for (int j = 0; j < k; ++j) { + int v, d; + scanf("%d %d", &v, &d); + g.addEdge(u, v-1, d); + } + } + ShortestPath solver(g); + int k; + scanf("%d", &k); + for (int i = 0; i < k; ++i) { + char s[1024], t[1024]; + scanf("%s %s", s, t); + printf("%d\n", solver.solve(id[s], id[t])); + } + } +} + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} +int test() { + int n = 5000; + WeightedGraph g(n); + for (int u = 0; u < n; ++u) { + int d = 1 + rand() % 30; + unordered_set N; + while (N.size() < d) { + int v = rand() % n; + if (u != v) N.insert(v); + } + for (auto v: N) { + int w = 1 + rand() % 10; + g.addEdge(u, v, w); + } + } + ShortestPath solver(g); + double t = 0; + for (int u = 0; u < n; ++u) { + int v = rand() % n; + tick(); + solver.solve(u); + int b = solver.dist[v]; + t += tick(); + } + cout << t << endl; + return true; +} + +int main() { + test(); +// SPOJ_SHPATH(); + /* + WeightedGraph g(3); + g.addEdge(0,1,10); + g.addEdge(0,2,20); + + ShortestPath solver(g); + solver.solve(0); + for (int u = 0; u < g.n; ++u) { + cout << solver.dist[u] << " "; + } + cout << endl; + */ +} diff --git a/graph/shortest_path_st.cc b/graph/shortest_path_st.cc deleted file mode 100644 index 1fbf1a9..0000000 --- a/graph/shortest_path_st.cc +++ /dev/null @@ -1,104 +0,0 @@ -// -// Single pair shortest path (Bidirectional dijkstra) -// -// Description: -// For a pair of vertices, it finds a shortest path between -// these two vertices. -// -// Algorithm: -// Bidirectional dijkstra algorithm that performs Dijkstra -// algorithm from s and t simultaneously. -// Usually, it is much faster than standard Dijkstra. -// -// Verified: -// SPOJ SHPATH -// -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -#define fst first -#define snd second - -const int INF = 99999999; -struct graph { - int n; - struct edge { int src, dst, weight; }; - vector> adj, rdj; - graph(int n) : n(n), adj(n), rdj(n) { } - void add_edge(int u, int v, int w) { - adj[u].push_back({u, v, w}); - rdj[v].push_back({v, u, w}); - } - - int shortest_path(int s, int t) { - if (s == t) return 0; - vector ds(n, INF), dt(n, INF); - typedef pair node; - priority_queue, greater> Qs, Qt; - Qs.push({ds[s] = 0, s}); - Qt.push({dt[t] = 0, t}); - int mu = INF; - while (!Qs.empty() && !Qt.empty()) { - if (Qs.top().fst + Qt.top().fst >= mu) break; - if (Qs.top().fst <= Qt.top().fst) { - node x = Qs.top(); Qs.pop(); - if (ds[x.snd] > x.fst) continue; - for (edge &e: adj[x.snd]) { - if (ds[e.src] + e.weight < ds[e.dst]) { - mu = min(mu, ds[e.src] + e.weight + dt[e.dst]); - Qs.push({ds[e.dst] = ds[e.src] + e.weight, e.dst}); - } - } - } else { - node x = Qt.top(); Qt.pop(); - if (dt[x.snd] > x.fst) continue; - for (edge &e: rdj[x.snd]) { - if (dt[e.src] + e.weight < dt[e.dst]) { - mu = min(mu, dt[e.src] + e.weight + ds[e.dst]); - Qt.push({dt[e.dst] = dt[e.src] + e.weight, e.dst}); - } - } - } - } - return mu; - } -}; - - -void solve() { - int n; - scanf("%d", &n); - graph g(n); - map id; - for (int u = 0; u < n; ++u) { - char name[1024]; - scanf("%s", name); - id[name] = u; - int k; - scanf("%d", &k); - for (int j = 0; j < k; ++j) { - int v, d; - scanf("%d %d", &v, &d); - g.add_edge(u, v-1, d); - } - } - int k; - scanf("%d", &k); - for (int i = 0; i < k; ++i) { - char s[1024], t[1024]; - scanf("%s %s", s, t); - printf("%d\n", g.shortest_path(id[s], id[t])); - } -} - -int main() { - int ncase; scanf("%d", &ncase); - for (int icase = 0; icase < ncase; ++icase) solve(); -} From a810c3f304ac79daf15376ea6f13ec3b1e786955 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:20:50 +0900 Subject: [PATCH 45/80] radix_heap.cc --- data_structure/radix_heap.cc | 182 ++++++++++++++++++++++++++++++----- 1 file changed, 160 insertions(+), 22 deletions(-) diff --git a/data_structure/radix_heap.cc b/data_structure/radix_heap.cc index 9685dfc..b23e526 100644 --- a/data_structure/radix_heap.cc +++ b/data_structure/radix_heap.cc @@ -1,9 +1,30 @@ -#include -#include -#include -#include -#include -#include +// +// Radix Heap +// +// Description: +// +// A radix heap is a heap for keys of type unsigned int. +// It runs in O(bit) time for each operations with a +// smaller constant factor. It is useful in Dijkstra +// shortest path algorithm with integral edge weights. +// see: https://en.wikipedia.org/wiki/Radix_heap +// +// Complexity: +// +// O(|bit|) per operations. +// +// Verified: +// +// SPOJ_SHPATH +// +// References: +// +// B. V. Cherkassky, A. V. Goldberg, C. Silverstein (1997): +// "Buckets, Heaps, Lists and Monotone Priority Queues." +// in Proceedings of the 8th Annual ACM-SIAM Symposium on +// Discrete Algorithms (SODA'97), pp. 83-92. +// +#include using namespace std; @@ -12,23 +33,23 @@ using namespace std; #define all(c) ((c).begin()), ((c).end()) #define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } -// min heap +// Min Heap template -struct radix_heap { - typedef unsigned int uint; +struct RadixHeap { + using uint = uint32_t; // fix bit size static int bsr(uint a) { return a ? 31 - __builtin_clz(a) : -1; } uint size, last; vector> v[33]; - radix_heap() : size(0), last(0) { } + RadixHeap() : size(0), last(0) { } bool empty() const { return size == 0; } - void aux(const pair &p) { v[bsr(p.fst^last)+1].push_back(p); } + void aux(pair p) { v[bsr(p.fst^last)+1].push_back(p); } pair top() { if (v[0].empty()) { int i = 1; while (v[i].empty()) ++i; last = min_element(all(v[i]))->fst; - for (auto &p: v[i]) aux(p); + for (auto p: v[i]) aux(p); v[i].clear(); } return v[0].back(); @@ -37,15 +58,132 @@ struct radix_heap { void pop() { --size; top(); v[0].pop_back(); } }; -int main() { - radix_heap heap; - heap.push(strlen("test"), "test"); - heap.push(strlen("a"), "a"); - heap.push(strlen("ab"), "ab"); - heap.push(strlen("aaa"), "aaa"); - heap.push(strlen("xyzz"), "xyzz"); - while (!heap.empty()) { - cout << heap.top().snd << endl; - heap.pop(); +template +struct WeightedGraph { + struct Edge { + int to; + Weight weight; + }; + int n; + vector> adj, rdj; + WeightedGraph(int n) : n(n), adj(n), rdj(n) { } + void addEdge(int u, int v, Weight w) { + adj[u].push_back({v, w}); + rdj[v].push_back({u, w}); // can be omitted for the standard dijkstra + } +}; + +template +struct ShortestPath { }; // omit + +template <> +struct ShortestPath { + using Weight = int; + WeightedGraph g; + + vector dist; + vector prev; + ShortestPath(WeightedGraph g) : g(g), dist(g.n), prev(g.n) { } + + void solve(int s) { + fill(all(prev), -1); + fill(all(dist), -1); dist[s] = 0; + + RadixHeap que; + que.push(0,s); + while (!que.empty()) { + auto d = que.top().fst; + auto u = que.top().snd; + que.pop(); + if (dist[u] < d) continue; + for (auto e: g.adj[u]) { + auto v = e.to; + auto w = e.weight; + if (dist[v] >= 0 && dist[v] <= dist[u]+w) continue; + dist[v] = dist[u] + w; + prev[v] = u; + que.push(dist[v], v); + } + } } + // Bidirectional Dijkstra + int solve(int s, int t) { + if (s == t) return dist[s] = 0; + + fill(all(dist), -1); dist[s] = 0; + vector drev(g.n, -1); drev[t] = 0; + RadixHeap qs, qt; + qs.push(0,s); qt.push(0,t); + int mu = -1; + while (!qs.empty() && !qt.empty()) { + if (mu >= 0 && qs.top().fst + qt.top().fst >= mu) break; + if (qs.top().fst <= qt.top().fst) { + auto d = qs.top().fst; + auto u = qs.top().snd; + qs.pop(); + if (dist[u] > d) continue; + for (auto e: g.adj[u]) { + auto v = e.to; + auto w = e.weight; + if (dist[v] >= 0 && dist[v] <= dist[u] + w) continue; + dist[v] = dist[u] + w; + qs.push(dist[v], v); + if (drev[v] >= 0) { + auto nu = dist[v] + drev[v]; + if (mu < 0 || mu > nu) mu = nu; + } + } + } else { + auto d = qt.top().fst; + auto u = qt.top().snd; + qt.pop(); + if (drev[u] > d) continue; + for (auto e: g.rdj[u]) { + auto v = e.to; + auto w = e.weight; + if (drev[v] >= 0 && drev[v] <= drev[u] + w) continue; + drev[v] = drev[u] + w; + qt.push(drev[v], v); + if (dist[v] >= 0) { + auto nu = dist[v] + drev[v]; + if (mu < 0 || mu > nu) mu = nu; + } + } + } + } + return mu; + } +}; + +void SPOJ_SHPATH() { + int ncase; scanf("%d", &ncase); + for (int icase = 0; icase < ncase; ++icase) { + int n; + scanf("%d", &n); + WeightedGraph g(n); + map id; + for (int u = 0; u < n; ++u) { + char name[1024]; + scanf("%s", name); + id[name] = u; + int k; + scanf("%d", &k); + for (int j = 0; j < k; ++j) { + int v, d; + scanf("%d %d", &v, &d); + g.addEdge(u, v-1, d); + } + } + ShortestPath solver(g); + int k; + scanf("%d", &k); + for (int i = 0; i < k; ++i) { + char s[1024], t[1024]; + scanf("%s %s", s, t); + printf("%d\n", solver.solve(id[s], id[t])); + } + } +} +int main() { + SPOJ_SHPATH(); } From ce25bade1e3388d5a7a79f2686a0263da957a9b9 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:28:09 +0900 Subject: [PATCH 46/80] Radix Heap --- data_structure/radix_heap.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/data_structure/radix_heap.cc b/data_structure/radix_heap.cc index b23e526..16c9b33 100644 --- a/data_structure/radix_heap.cc +++ b/data_structure/radix_heap.cc @@ -3,15 +3,16 @@ // // Description: // -// A radix heap is a heap for keys of type unsigned int. -// It runs in O(bit) time for each operations with a -// smaller constant factor. It is useful in Dijkstra -// shortest path algorithm with integral edge weights. +// A radix heap is a monotonic heap for keys of type unsigned int. +// Here, a monotonic heap is a heap that allows push(key) only for +// key >= top(). It is useful in the Dijkstra shortest path algorithm +// with integral edge weights. // see: https://en.wikipedia.org/wiki/Radix_heap // // Complexity: // -// O(|bit|) per operations. +// O(|bit|) for all operations. Moreover, +// O(1), amortized, for pop. // // Verified: // From f8c90281baef8950ec1b484507dce64ed6e2f92b Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:37:09 +0900 Subject: [PATCH 47/80] Numerical Differentiation --- numerical/derivative.cc | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 numerical/derivative.cc diff --git a/numerical/derivative.cc b/numerical/derivative.cc new file mode 100644 index 0000000..9b700bd --- /dev/null +++ b/numerical/derivative.cc @@ -0,0 +1,58 @@ +// +// Numerical Derivative by Ridder's method. +// + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +template +double differentiate(F f, double x, double eps = 1e-8) { + const int n = 10; + const double alpha = 1.4; + double h = 1e-2, a[n][n], ans = 1.0/0.0, err = 1.0/0.0; + + a[0][0] = (f(x + h) - f(x - h)) / (2 * h); + for (int i = 1; i < n; ++i) { + h /= alpha; + a[0][i] = (f(x + h) - f(x - h))/(2 * h); + double fac = alpha * alpha; + for (int j = 1; j <= i; ++j) { + a[j][i] = (a[j-1][i] * fac - a[j-1][i-1])/(fac - 1.0); + fac *= alpha * alpha; + double errt = max(fabs(a[j][i] - a[j-1][i]), fabs(a[j][i] - a[j-1][i-1])); + if (errt <= err) { + err = errt; + ans = a[j][i]; + if (err < eps) return ans; + } + } + if (fabs(a[i][i] - a[i-1][i-1]) >= 2 * err) break; + } + return ans; +} + +double f(double x) { + return exp(-x*x); +} +double df(double x) { + return -2 * x * exp(-x*x); +} + +int main() { + for (int i = 0; i < 10; ++i) { + double x = rand() / (1.0 + RAND_MAX); + cout << differentiate(f, x) - df(x) << endl; + } +} + From 4050b9aec396e1381e6a65853c67e64fd7fe096a Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:38:08 +0900 Subject: [PATCH 48/80] Numerical Differentiation --- {numerical => numeric}/derivative.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {numerical => numeric}/derivative.cc (100%) diff --git a/numerical/derivative.cc b/numeric/derivative.cc similarity index 100% rename from numerical/derivative.cc rename to numeric/derivative.cc From 54eff6e49df09bf337975d40c90c5b3e5961e541 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:38:42 +0900 Subject: [PATCH 49/80] Ordinal Differential Equation (Dormand-Prince) --- {math => numeric}/ODE_dormand_prince.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => numeric}/ODE_dormand_prince.cc (100%) diff --git a/math/ODE_dormand_prince.cc b/numeric/ODE_dormand_prince.cc similarity index 100% rename from math/ODE_dormand_prince.cc rename to numeric/ODE_dormand_prince.cc From 9a3ab6aa375603dda63885f38451c9e521c7c55b Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:39:09 +0900 Subject: [PATCH 50/80] Ordinal Differential Equation (Runge Kutta) --- {math => numeric}/ODE_runge_kutta.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => numeric}/ODE_runge_kutta.cc (100%) diff --git a/math/ODE_runge_kutta.cc b/numeric/ODE_runge_kutta.cc similarity index 100% rename from math/ODE_runge_kutta.cc rename to numeric/ODE_runge_kutta.cc From f2f0cb20608bbc5469a3100ef0fea678f86a3cb6 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:39:30 +0900 Subject: [PATCH 51/80] Delete derivative.cc --- math/derivative.cc | 58 ---------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 math/derivative.cc diff --git a/math/derivative.cc b/math/derivative.cc deleted file mode 100644 index 9b700bd..0000000 --- a/math/derivative.cc +++ /dev/null @@ -1,58 +0,0 @@ -// -// Numerical Derivative by Ridder's method. -// - -#include -#include -#include -#include -#include -#include - -using namespace std; - -#define fst first -#define snd second -#define all(c) ((c).begin()), ((c).end()) -#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } - -template -double differentiate(F f, double x, double eps = 1e-8) { - const int n = 10; - const double alpha = 1.4; - double h = 1e-2, a[n][n], ans = 1.0/0.0, err = 1.0/0.0; - - a[0][0] = (f(x + h) - f(x - h)) / (2 * h); - for (int i = 1; i < n; ++i) { - h /= alpha; - a[0][i] = (f(x + h) - f(x - h))/(2 * h); - double fac = alpha * alpha; - for (int j = 1; j <= i; ++j) { - a[j][i] = (a[j-1][i] * fac - a[j-1][i-1])/(fac - 1.0); - fac *= alpha * alpha; - double errt = max(fabs(a[j][i] - a[j-1][i]), fabs(a[j][i] - a[j-1][i-1])); - if (errt <= err) { - err = errt; - ans = a[j][i]; - if (err < eps) return ans; - } - } - if (fabs(a[i][i] - a[i-1][i-1]) >= 2 * err) break; - } - return ans; -} - -double f(double x) { - return exp(-x*x); -} -double df(double x) { - return -2 * x * exp(-x*x); -} - -int main() { - for (int i = 0; i < 10; ++i) { - double x = rand() / (1.0 + RAND_MAX); - cout << differentiate(f, x) - df(x) << endl; - } -} - From e48dcd84c51378ee2df0197ca3ba7e56709b1400 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:40:33 +0900 Subject: [PATCH 52/80] Assignment Problem (Jonker-Volgenant) --- {math => numeric}/assignment.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => numeric}/assignment.cc (100%) diff --git a/math/assignment.cc b/numeric/assignment.cc similarity index 100% rename from math/assignment.cc rename to numeric/assignment.cc From 249fe566326553a5c370c4c9c226d0d43edb2859 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:41:36 +0900 Subject: [PATCH 53/80] Numerical Integrator (Adaptive Gauss-Lobatto) --- {math => numeric}/integrate.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => numeric}/integrate.cc (100%) diff --git a/math/integrate.cc b/numeric/integrate.cc similarity index 100% rename from math/integrate.cc rename to numeric/integrate.cc From 7f295461723c0fc939a7937d40a196889dc92ec6 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:42:31 +0900 Subject: [PATCH 54/80] Numerical Integrator (Double Exponential) --- {math => numeric}/integrate_DE.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => numeric}/integrate_DE.cc (100%) diff --git a/math/integrate_DE.cc b/numeric/integrate_DE.cc similarity index 100% rename from math/integrate_DE.cc rename to numeric/integrate_DE.cc From c3c80f8ea0038b735d2d0b07743925b6083a9b71 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:44:25 +0900 Subject: [PATCH 55/80] Goldensection Search for 1D Unimodal Minimization --- {math => numeric}/find_min_unimodal.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => numeric}/find_min_unimodal.cc (100%) diff --git a/math/find_min_unimodal.cc b/numeric/find_min_unimodal.cc similarity index 100% rename from math/find_min_unimodal.cc rename to numeric/find_min_unimodal.cc From 9cf6552460342fd277c74c696b471760a848ef5c Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:45:44 +0900 Subject: [PATCH 56/80] Nelder Mead Method for Non-convex Minimization --- {math => numeric}/nelder_mead.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {math => numeric}/nelder_mead.cc (98%) diff --git a/math/nelder_mead.cc b/numeric/nelder_mead.cc similarity index 98% rename from math/nelder_mead.cc rename to numeric/nelder_mead.cc index f48bb0d..bd24796 100644 --- a/math/nelder_mead.cc +++ b/numeric/nelder_mead.cc @@ -2,7 +2,7 @@ // Nelder Mead method (aka. Downhill Simplex Method) // // Description: -// Nelder Mead method is a first-order optimization method +// Nelder Mead method is a zeroth-order optimization method // that only requires function evaluation oracle. // Typically, it performs well on a function on a small // dimensional space. From 0890ecc39e2911334d17fee4fd2c28cc680243cd Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:46:33 +0900 Subject: [PATCH 57/80] Automatic Differentiation by Dual Numbers --- {math => numeric}/dual_number.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => numeric}/dual_number.cc (100%) diff --git a/math/dual_number.cc b/numeric/dual_number.cc similarity index 100% rename from math/dual_number.cc rename to numeric/dual_number.cc From 954491aca85b3bf6911174554ce9a521267d356e Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 14 Jan 2018 22:53:53 +0900 Subject: [PATCH 58/80] Chebyshev Approximation --- numeric/chebyshev.cc | 125 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 numeric/chebyshev.cc diff --git a/numeric/chebyshev.cc b/numeric/chebyshev.cc new file mode 100644 index 0000000..c2b5b0e --- /dev/null +++ b/numeric/chebyshev.cc @@ -0,0 +1,125 @@ +// +// Chebyshev approximation of smooth function +// +// Description: +// +// Given a smooth function f: [0,1] -> R. It approximates f by +// f(x) \sim p(x) := sum_{k=0}^{n-1} ck Tk(x) +// where Tk(x) is the Chebyshev polynomial of the first kind: +// Tk(cos(u)) = cos(ku). +// This almost minimizes the maximum error: +// sup_x | f(x) - p(x) | +// This also provides accurate dp/dx and \int_{a^x} p(u) du. +// +// We can construct the Chebyshev approximation in O(n^2) time +// by using the orthogonality with Clenshow's relation. +// +// Complexity: +// +// O(n^2), where n is usually 10--30. +// +// References: +// +// C. W. Clenshaw (1995): +// "A note on the summation of Chebyshev series." +// Mathematical Tables and Other Aids to Computation. +// vol.9, no.51, pp.118--120. +// + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +using Real = long double; +struct Chebyshev { + static constexpr Real PI = acos(-1.0); + int n; + Real a, b; + vector c; + Chebyshev(Real a, Real b, int n) : n(n), a(a), b(b), c(n) { } + + template + Chebyshev(F f, Real a, Real b, int n = 20) : n(n), a(a), b(b), c(n) { + vector h(n); + for (int k = 0; k < n; ++k) { + Real y = cos(PI*(k+0.5)/n); + h[k] = f((b-a)/2*y + (b+a)/2); + } + for (int j = 0; j < n; ++j) { + for (int k = 0; k < n; ++k) + c[j] += h[k] * cos(PI*j*(k+0.5)/n); + c[j] *= 2.0/n; + } + } + Real operator()(Real x) const { + Real y = (2*x - a-b)/(b-a), u = 0, v = 0; + for (int j = n-1; j >= 1; --j) { + Real w = 2*y*u - v + c[j]; + v = u; u = w; + } + return y*u - v + 0.5*c[0]; + } +}; +Chebyshev differentiate(Chebyshev f) { + Chebyshev g = f; + g.c[f.n-2] = 2 * (f.n-1) * f.c[f.n-1]; + for (int j = f.n-3; j >= 0; --j) + g.c[j] = g.c[j+2] + 2 * (j+1) * f.c[j+1]; + for (int j = 0; j < g.n; ++j) + g.c[j] *= 2.0/(g.b - g.a); + return g; +} +Chebyshev integrate(Chebyshev f) { + Chebyshev g = f; + Real sum = 0, coef = (f.b-f.a)/4, sign = 1.0; + for (int j = 1; j <= g.n-2; ++j) { + g.c[j] = coef * (f.c[j-1] - f.c[j+1]) / j; + sum += sign * g.c[j]; + sign = -sign; + } + g.c[f.n-1] = coef * f.c[f.n-2] / (f.n-1); + sum += sign * g.c[f.n-1]; + g.c[0] = 2 * sum; + return g; +} + +int main() { + auto f = [&](Real x) { + return sqrt(1 + x*x); + }; + auto df = [&](Real x) { + return x / sqrt(1 + x*x); + }; + auto F = [&](Real x) { + return (x*sqrt(1 + x*x) + asinh(x))/2; + }; + Chebyshev g(f, 0, 1, 10); + Chebyshev dg = differentiate(g); + Chebyshev G = integrate(g); + + cout << "---" << endl; + for (int i = 0; i < 10; ++i) { + Real u = i / 10.0; + cout << f(u) - g(u) << endl; + } + cout << "---" << endl; + for (int i = 0; i < 10; ++i) { + Real u = i / 10.0; + cout << df(u) - dg(u) << endl; + } + cout << "---" << endl; + for (int i = 0; i < 10; ++i) { + Real u = i / 10.0; + cout << F(u) - G(u) << endl; + } +} From b74dffcead5ca26b7f4fd0a1b6515c5d1669943c Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 27 Jan 2018 14:46:25 +0900 Subject: [PATCH 59/80] Parallel Binary Search --- data_structure/parallel_binary_search.cc | 149 +++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 data_structure/parallel_binary_search.cc diff --git a/data_structure/parallel_binary_search.cc b/data_structure/parallel_binary_search.cc new file mode 100644 index 0000000..1323f80 --- /dev/null +++ b/data_structure/parallel_binary_search.cc @@ -0,0 +1,149 @@ +// +// Parallel Binary Search +// +// Description: +// +// Let W(t) be a data structure depending on time t. +// Suppose that there are n agents. Each agent j wants to +// find the smallest time t(j) such that cond(j, W(t(j))) == true +// where cond(j, W(t)) is monotone in t. +// +// If we perform n binary searches independently, we will construct +// W(t) multiple times. Thus, we avoid the redundant construction +// by performing n binary searches in parallel. Imagine a binary +// search tree on t. For each node, we first construct W(t). +// Then we process multiple agents in parallel. Then, the total +// number of constructions is O(log T), which is independent to +// the number of agents. +// +// Complexity: +// +// Suppose that W(t) is constructed from W(t') in time M(|t-t'|), +// and the condition cond(j,W(t)) is evaluated in time Q. +// Then, it runs in O(M(T log T) + n Q log T) time. +// +// Even if W does not have decremental operations, i.e., W(t) +// cannot be constructed from W(t') with t' > t, we can still use +// the parallel binary search that runs in O(M(T) log T + n Q log T); +// time. The constant factor is twice worse than the above. +// Similar result holds if W does not have incremental operations. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +using Int = __int128_t; + +// Point Query, Range Update +template +struct FenwickTree { + vector x; + FenwickTree(int n) : x(n) { } + void add(int k, T a) { // aux + for (; k < x.size(); k |= k+1) x[k] += a; + } + void add(int i, int j, T a) { // add x[k] += a for all k in [i,j] + add(i, a); + if (j+1 < x.size()) add(j+1, -a); + } + T get(int k) { // return x[k] + T sum = 0; + for (; k >= 0; k = (k&(k+1))-1) sum += x[k]; + return sum; + } +}; + +template +vector parallelBinarySearch( + int n, int lo, int hi, Update update, Cond cond) { + using It = vector::iterator; + vector agents(n), solution(n, lo); + iota(all(agents), 0); + + It begin = agents.begin(), end = agents.end(); + deque> stack = {make_tuple(lo, hi, begin, end)}; + while (!stack.empty()) { + // invariant: elems in [begin, end) satisfy "!cond(lo) and cond(hi)" + tie(lo, hi, begin, end) = stack.back(); + stack.pop_back(); + + if (begin == end) continue; + if (lo+1 == hi) { + for_each(begin, end, [&](int k) { solution[k] = hi; }); + continue; + } + int mi = (lo + hi) / 2; + update(mi); + It mid = partition(begin, end, [&](int k) { return cond(k); }); + stack.push_back(make_tuple(mi, hi, mid, end)); + stack.push_back(make_tuple(lo, mi, begin, mid)); + } + return solution; +} + +void SPOJ_METEORS() { + int n, m, k; + scanf("%d %d", &n, &m); + vector> S(n); + vector p(n); + for (int i = 0; i < m; ++i) { + int j; scanf("%d", &j); + S[j-1].push_back(i); + } + for (int i = 0; i < n; ++i) + scanf("%lld", &p[i]); + scanf("%d", &k); + vector l(k), r(k); + vector a(k); + for (int i = 0; i < k; ++i) { + scanf("%d %d %lld", &l[i], &r[i], &a[i]); + --l[i]; --r[i]; + } + + FenwickTree FT(m); + int curr = -1; + auto update = [&](int time) { + while (curr < time) { + ++curr; + if (l[curr] <= r[curr]) { + FT.add(l[curr], r[curr], a[curr]); + } else { + FT.add(l[curr], m-1, a[curr]); + FT.add(0, r[curr], a[curr]); + } + } + while (curr > time) { + if (l[curr] <= r[curr]) { + FT.add(l[curr], r[curr], -a[curr]); + } else { + FT.add(l[curr], m-1, -a[curr]); + FT.add(0, r[curr], -a[curr]); + } + --curr; + } + }; + // minimum time such that cond = true. + auto cond = [&](int j) { + Int total = 0; + for (int i: S[j]) { + total += FT.get(i); + } + return total >= p[j]; + }; + + auto solution = parallelBinarySearch(n, -1, k, update, cond); + + for (int i = 0; i < n; ++i) { + if (solution[i] >= k) cout << "NIE" << endl; + else cout << 1+solution[i] << endl; + } +} + +int main() { + SPOJ_METEORS(); +} From b541e0b06aa4145592527b7de8e2caa8720bac22 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 28 Jan 2018 09:19:38 +0900 Subject: [PATCH 60/80] Bjorklund-Husfield's exact chromatic number --- graph/chromatic_number.cc | 96 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 graph/chromatic_number.cc diff --git a/graph/chromatic_number.cc b/graph/chromatic_number.cc new file mode 100644 index 0000000..aef4837 --- /dev/null +++ b/graph/chromatic_number.cc @@ -0,0 +1,96 @@ +// +// Exact Algorithm for Chromatic Number +// +// Description: +// +// A vertex coloring is an assignment of colors to the vertices +// such that no adjacent vertices have a same color. The smallest +// number of colors for a vertex coloring is called the chromatic +// number. Computing the chromatic number is NP-hard. +// +// We can compute the chromatic number by the inclusion-exlusion +// principle. The complexity is O(poly(n) 2^n). The following +// implementation runs in O(n 2^n) but is a Monte-Carlo algorithm +// since it takes modulos to avoid multiprecision numbers. +// +// Complexity: +// +// O(n 2^n) +// +// References: +// +// Andreas Bjorklund and Thore Husfeldt (2006): +// "Inclusion--Exclusion Algorithms for Counting Set Partitions." +// in Proceedings of the 47th Annual Symposium on Foundations of +// Computer Science, pp. 575--582. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +struct Graph { + int n; + vector> adj; + Graph(int n) : n(n), adj(n) { } + void addEdge(int u, int v) { + adj[u].push_back(v); + adj[v].push_back(u); + } +}; + +int chromaticNumber(Graph g) { + const int N = 1 << g.n; + vector nbh(g.n); + for (int u = 0; u < g.n; ++u) + for (int v: g.adj[u]) + nbh[u] |= (1 << v); + + int ans = g.n; + for (int d: {7}) { // ,11,21,33,87,93}) { + long long mod = 1e9 + d; + vector ind(N), aux(N, 1); + ind[0] = 1; + for (int S = 1; S < N; ++S) { + int u = __builtin_ctz(S); + ind[S] = ind[S^(1<> 1); // gray-code + aux[S] = (aux[S] * ind[S]) % mod; + chi += (i & 1) ? aux[S] : -aux[S]; + } + if (chi % mod) ans = k; + } + } + return ans; +} + +int main() { + int n = 6; + Graph g(n); + g.addEdge(0,1); + g.addEdge(1,2); + g.addEdge(2,3); + g.addEdge(0,2); + g.addEdge(3,4); + g.addEdge(4,5); + g.addEdge(5,0); + // 0 + // 1 5 + // + // 2 4 + // 3 + /* + for (int i = 0; i < n; ++i) + for (int j = 0; j < i; ++j) + g.addEdge(i, j); + */ + cout << chromaticNumber(g) << endl; +} From 73aab4cd5d92c370049d3c55fd8ebacb67554476 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 28 Jan 2018 17:48:10 +0900 Subject: [PATCH 61/80] Modular Arithmetics --- math/modular_arithmetics.cc | 467 ++++++++++++++++++++++++++++++++++++ 1 file changed, 467 insertions(+) create mode 100644 math/modular_arithmetics.cc diff --git a/math/modular_arithmetics.cc b/math/modular_arithmetics.cc new file mode 100644 index 0000000..70d495a --- /dev/null +++ b/math/modular_arithmetics.cc @@ -0,0 +1,467 @@ +// +// Modular Arithmetics +// +// long long: 10^18 < 2^63-1 < 10^19 (strict inequality) +// __int128_t: 10^38 < 2^127-1 < 10^39 +// +// g++ -std=c++17 -O3 -fmax-errors=1 -fsanitize=undefined +#include +#pragma GCC optimize ("O3") + +using namespace std; +#define fst first +#define snd second +#define all(c) begin(c), end(c) + + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} + + +template +ostream &operator<<(ostream &os, const vector &v) { + os << "["; + for (int i = 0; i < v.size(); os << v[i++]) + if (i > 0) os << " "; + os << "]"; + return os; +} +template +ostream &operator<<(ostream &os, const vector> &v) { + os << "["; + for (int i = 0; i < v.size(); os << v[i++]) + if (i > 0) os << endl << " "; + os << "]"; + return os; +} +using Int = long long; +struct ModInt { + Int val, mod; + ModInt(Int v, Int m) : val(v), mod(m) { } + ModInt operator-() const { return ModInt(val?mod-val:val,mod); } + ModInt &operator+=(ModInt a) { + if ((val += a.val) >= mod) val -= mod; + return *this; + } + ModInt &operator-=(ModInt a) { + if ((val -= a.val) < 0) val += mod; + return *this; + } + ModInt &operator*=(ModInt a) { + val = (__uint128_t(val) * a.val) % mod; + return *this; + } + ModInt &operator/=(ModInt a) { + Int u = 1, v = a.val, s = 0, t = mod; + while (v) { + Int q = t / v; + swap(s -= u * q, u); + swap(t -= v * q, v); + } + a.val = (s < 0 ? s + mod : s); + val /= t; + return (*this) *= a; + } + ModInt inv() const { return ModInt(1,mod) /= (*this); } + bool operator<(ModInt x) const { return val < x.val; } +}; +// ModInt modInt(Int v) { return ModInt(v,MOD); } +ostream &operator<<(ostream &os, ModInt a) { os << a.val; return os; } +ModInt operator+(ModInt a, ModInt b) { return a += b; } +ModInt operator-(ModInt a, ModInt b) { return a -= b; } +ModInt operator*(ModInt a, ModInt b) { return a *= b; } +ModInt operator/(ModInt a, ModInt b) { return a /= b; } +ModInt pow(ModInt a, Int e) { + ModInt x(1, a.mod); + for (; e > 0; e /= 2) { + if (e % 2 == 1) x *= a; + a *= a; + } + return x; +} +ModInt stringToModInt(string s, Int mod) { + Int val = 0; + for (int i = 0; i < s.size(); ++i) + val = (val*10 + (s[i]-'0')) % mod; + return ModInt(val, mod); +} + + +// compute inv[1], inv[2], ..., inv[mod-1] in O(n) time +vector inverse(Int mod) { + vector inv(mod, ModInt(0, mod)); + inv[1] = 1; + for (Int a = 2; a < mod; ++a) + inv[a] = inv[mod % a] * (mod - mod/a); + return inv; +} + + +// +// Solve x^2 = n; mod should be a prime +// +// Verified: Code Forces Quadratic Equations +// +bool isQuadraticResidue(ModInt n) { + return n.val == 0 || n.mod == 2 || pow(n, (n.mod-1)/2).val == 1; +} +ModInt sqrt(ModInt n) { + if (n.val == 0 || n.mod == 2) return n; + int M = __builtin_ctz(n.mod-1), Q = (n.mod-1)>>M; + ModInt z(2, n.mod); + while (isQuadraticResidue(z)) ++z.val; + ModInt c = pow(z, Q); + ModInt t = pow(n, Q); + ModInt R = pow(n, (Q+1)/2); + while (t.val != 1) { + int i = 0; + for (ModInt s = t; s.val != 1; s *= s) ++i; + if (M == i) exit(0); + ModInt b = pow(c, 1<<(M-i-1)); + M = i; + c = b*b; + t *= c; + R *= b; + } + return R; +} +vector quadraticEquation(ModInt a, ModInt b, ModInt c) { + if (a.mod == 2) { + vector ans; + if (c.val == 0) ans.push_back(c); + if ((a + b + c).val == 0) ans.push_back(ModInt(1,2)); + return ans; + } else { + b /= (a+a); c /= a; + ModInt D = b*b - c; + if (!isQuadraticResidue(D)) return {}; + ModInt s = sqrt(D), x = -b+s, y = -b-s; + return (x.val < y.val) ? vector({x, y}) : + (x.val > y.val) ? vector({y, x}) : + vector({x}); + } +} + +// Discrete Logarithm by Shanks' Baby-Step Giant-Step +// +// Find k such that a^k == b +// +Int log(ModInt a, ModInt b) { + Int h = ceil(sqrt(a.mod+1e-9)); + unordered_map hash; + ModInt x(1, a.mod); + for (Int i = 0; i < h; ++i) { + if (!hash.count(x.val)) hash[x.val] = i; + x *= a; + } + x = x.inv(); + ModInt y = b; + for (int i = 0; i < h; ++i) { + if (hash.count(y.val)) return i*h+hash[y.val]; + y *= x; + } + return -1; +} + + +// +// find solution z.val such that +// z.val == x.val (mod x.mod), for all x. +// the solution is unique in modulo z.mod. +// +Int extgcd(Int a, Int b, Int&x, Int&y) { + for (Int u = y = 1, v = x = 0; a; ) { + Int q = b / a; + swap(x -= q * u, u); + swap(y -= q * v, v); + swap(b -= q * a, a); + } + return b; // a x + b y == gcd(a, b) +} +ModInt chineseRemainder(vector modular) { + ModInt z(0, 1); // z == 0 (mod 1) + for (ModInt x: modular) { + Int u, v, g = extgcd(x.mod, z.mod, u, v); + z.val = z.val*u*x.mod + x.val*v*z.mod; + z.mod = z.mod * (x.mod / g); + } + if ((z.val %= z.mod) < 0) z.val += z.mod; + return z; +} + +struct ModMatrix { + int m, n; // m times n matrix + vector> val; + Int mod; + ModInt &operator()(int i, int j) { return val[i][j]; } + ModMatrix(int m, int n, Int mod) : + m(m), n(n), mod(mod), val(m, vector(n, ModInt(0,mod))) { } + ModMatrix operator-() const { + ModMatrix A(m, n, mod); + for (int i = 0; i < m; ++i) + for (int j = 0; j < n; ++j) + A.val[i][j] = -val[i][j]; + return A; + } + ModMatrix &operator+=(ModMatrix A) { + for (int i = 0; i < m; ++i) + for (int j = 0; j < n; ++j) + val[i][j] += A.val[i][j]; + return *this; + } + ModMatrix &operator-=(ModMatrix A) { + for (int i = 0; i < m; ++i) + for (int j = 0; j < n; ++j) + val[i][j] -= A.val[i][j]; + return *this; + } + ModMatrix &operator*=(ModMatrix A) { + for (int i = 0; i < m; ++i) { + vector row(A.n, ModInt(0, A.mod)); + for (int j = 0; j < A.n; ++j) { + for (int k = 0; k < A.m; ++k) + row[j] += val[i][k] * A.val[k][j]; + } + val[i] = row; + } + return *this; + } + static ModMatrix eye(int n, Int mod) { + ModMatrix I(n, n, mod); + for (int i = 0; i < n; ++i) I.val[i][i].val = 1; + return I; + } + static ModMatrix zero(int n, Int mod) { + return ModMatrix(n, n, mod); + } + // Gauss-Jordan-Blankinship Elimination + // It can be used for any composite modulo (Euclidean domain). + ModMatrix inv() const { + ModMatrix B = eye(n, mod); + vector> a = val; + vector> &b = B.val; + for (int j = 0; j < n; ++j) { + // minimum nonzero をとって + // Blankinship type の吐き出しを行う + for (int i = 0; i < n; ++i) { + if (i == j) continue; + while (a[i][j].val) { + ModInt t(a[j][j].val/a[i][j].val, mod); + for (int k = j; k < n; ++k) swap(a[i][k], a[j][k]-=t*a[i][k]); + for (int k = 0; k < n; ++k) swap(b[i][k], b[j][k]-=t*b[i][k]); + } + } + if (a[j][j].val == 0) return ModMatrix(0,0,0); + } + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + cout << a[i][j] << " "; + } + cout << endl + } + + return B; + } + // It can be used for any composite modulo. + ModInt det() const { + vector> a = val; + ModInt D(1, mod); + for (int j = 0; j < n; ++j) { + for (int i = j+1; i < n; ++i) { + while (a[i][j].val) { + D = -D; + ModInt t(a[j][j].val/a[i][j].val, mod); + for (int k = j; k < n; ++k) + swap(a[i][k], a[j][k] -= t * a[i][k]); + } + } + D *= a[j][j]; + } + return D; + } +}; +ModMatrix operator+(ModMatrix A, ModMatrix B) { return A += B; } +ModMatrix operator-(ModMatrix A, ModMatrix B) { return A -= B; } +ModMatrix operator*(ModMatrix A, ModMatrix B) { return A *= B; } +ModMatrix pow(ModMatrix A, int k) { + ModMatrix X = ModMatrix::eye(A.n, A.mod); + for (; k > 0; k /= 2) { + if (k % 2 == 1) X *= A; + A *= A; + } + return X; +} +ModInt dot(ModMatrix A, ModMatrix B) { + ModInt val(0, A.mod); + for (int i = 0; i < A.m; ++i) + for (int j = 0; j < A.n; ++j) + val += A.val[i][j] * B.val[i][j]; + return val; +} +using ModVector = vector; +ModVector operator*(ModMatrix A, ModVector x) { + vector y(A.m, ModInt(0, A.mod)); + for (int i = 0; i < A.m; ++i) + for (int j = 0; j < A.n; ++j) + y[i] += A.val[i][j] * x[j]; + return y; +} + + +// +// Only available for prime modulos. +// If you want to compute multiple inverses, +// use LU decomposition instead of computing the inverse. +// +struct LUDecomposition { + int n; + vector pi; + vector> val; + Int mod; + LUDecomposition(ModMatrix A) : n(A.n), val(A.val), mod(A.mod) { + pi.resize(n+1); + iota(all(pi), 0); + for (int i = 0, j, k; i < n; ++i) { + for (k = i; k < n; ++k) + if (val[k][i].val) break; + if (k == n) { pi[n] = -1; return; } // NG + if (k != i) { + swap(pi[i], pi[k]); + swap(val[i], val[k]); + ++pi[n]; + } + for (j = i+1; j < n; ++j) { + if (val[j][i].val == 0) continue; + val[j][i]/= val[i][i]; + for (k = i+1; k < n; ++k) + val[j][k] -= val[j][i] * val[i][k]; + } + } + } + bool isRegular() const { return pi[n] >= 0; } + ModVector solve(ModVector b) { + vector x(b.size(), ModInt(0, mod)); + for (int i = 0; i < n; ++i) { + x[i] = b[pi[i]]; + for (int k = 0; k < i; ++k) + x[i] -= val[i][k] * x[k]; + } + for (int i = n-1; i >= 0; --i) { + for (int k = i+1; k < n; ++k) + x[i] -= val[i][k] * x[k]; + x[i] /= val[i][i]; + } + return x; + } + ModMatrix inverse() { // do not compute the inverse + ModMatrix B(n, n, mod); + for (int j = 0; j < n; ++j) { + for (int i = 0; i < n; ++i) { + if (pi[i] == j) B.val[i][j].val = 1; + for (int k = 0; k < i; k++) + B.val[i][j] -= val[i][k] * B.val[k][j]; + } + for (int i = n-1; i >= 0; --i) { + for (int k = i+1; k < n; ++k) + B.val[i][j] -= val[i][k] * B.val[k][j]; + B.val[i][j] /= val[i][i]; + } + } + return B; + } + ModInt det() { + ModInt D = val[0][0]; + for (int i = 1; i < n; i++) + D *= val[i][i]; + return ((pi[n] - n) % 2 != 0) ? -D : D; + } +}; + +void mulTest() { + Int mod = 1e9+7; + int m = 4, n = 4; + ModMatrix A(m,n,mod); + ModMatrix B(m,n,mod); + vector b(n, ModInt(0, mod)); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + A(i,j).val = rand() % mod; + B(i,j).val = rand() % mod; + } + b[i].val = rand() % mod; + } + ModMatrix C = A * B; + + for (int i = 0; i < C.m; ++i) { + for (int j = 0; j < C.n; ++j) { + cout << C(i,j) << " "; + } + cout << endl; + } + cout << C.det() << endl; + + LUDecomposition LU(C); + cout << LU.det() << endl; + + // A^{-1} b = x + auto x = LU.solve(b); + cout << b << " " << (C * x) << endl; + cout << "end" << endl; +} + +void CF_QUADRATIC_EQUATIONS() { + int ncase; cin >> ncase; + for (int icase = 0; icase < ncase; ++icase) { + Int a, b, c, p; + cin >> a >> b >> c >> p; + vector ans = quadraticEquation( + ModInt(a,p), ModInt(b,p), ModInt(c,p)); + cout << ans.size(); + for (int i = 0; i < ans.size(); ++i) + cout << " " << ans[i].val; + cout << endl; + } +} + +void CF_DISCLOG() { + ModInt a(21309,999998999999), b(696969,999998999999); + cout << log(a, b) << endl; +} + +int SPOJ_MIFF() { + for (int icase = 0; ; ++icase) { + int n, p; scanf("%d %d", &n, &p); + if (n == 0 && p == 0) break; + if (icase > 0) printf("\n"); + ModMatrix A(n, n, p); + for (int i = 0; i < n; ++i) + for (int j = 0; j < n; ++j) + scanf("%d", &A(i,j)); + + ModMatrix B = A.inv(); + if (B.m > 0) { + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + printf("%d ", B(i,j)); + } + printf("\n"); + } + } else { + printf("singular\n"); + } + } +} + +int main() { + SPOJ_MIFF(); + //CF_DISCLOG(); + //CF_QUADRATIC_EQUATIONS(); + //mulTest(); +} From b0b42c6f6e986d46263cab2b0cc1fbfbc35d5512 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 28 Jan 2018 17:49:39 +0900 Subject: [PATCH 62/80] Modular Arithmetics --- math/modular_arithmetics.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/math/modular_arithmetics.cc b/math/modular_arithmetics.cc index 70d495a..93bd23f 100644 --- a/math/modular_arithmetics.cc +++ b/math/modular_arithmetics.cc @@ -121,15 +121,15 @@ ModInt sqrt(ModInt n) { ModInt t = pow(n, Q); ModInt R = pow(n, (Q+1)/2); while (t.val != 1) { - int i = 0; + int i = 0; for (ModInt s = t; s.val != 1; s *= s) ++i; if (M == i) exit(0); ModInt b = pow(c, 1<<(M-i-1)); - M = i; + M = i; c = b*b; t *= c; - R *= b; - } + R *= b; + } return R; } vector quadraticEquation(ModInt a, ModInt b, ModInt c) { From 1a0a30bef08eab71d08dc25d61766de90cd9fcd7 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 28 Jan 2018 18:29:37 +0900 Subject: [PATCH 63/80] Modular Arithmetics --- math/modular_arithmetics.cc | 40 ++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/math/modular_arithmetics.cc b/math/modular_arithmetics.cc index 93bd23f..ddd61c6 100644 --- a/math/modular_arithmetics.cc +++ b/math/modular_arithmetics.cc @@ -97,9 +97,9 @@ ModInt stringToModInt(string s, Int mod) { // compute inv[1], inv[2], ..., inv[mod-1] in O(n) time vector inverse(Int mod) { vector inv(mod, ModInt(0, mod)); - inv[1] = 1; + inv[1].val = 1; for (Int a = 2; a < mod; ++a) - inv[a] = inv[mod % a] * (mod - mod/a); + inv[a] = inv[mod % a] * ModInt(mod - mod/a,mod); return inv; } @@ -241,32 +241,26 @@ struct ModMatrix { static ModMatrix zero(int n, Int mod) { return ModMatrix(n, n, mod); } - // Gauss-Jordan-Blankinship Elimination - // It can be used for any composite modulo (Euclidean domain). + // mod should be prime ModMatrix inv() const { ModMatrix B = eye(n, mod); - vector> a = val; + vector> a = val; vector> &b = B.val; - for (int j = 0; j < n; ++j) { - // minimum nonzero をとって - // Blankinship type の吐き出しを行う - for (int i = 0; i < n; ++i) { - if (i == j) continue; - while (a[i][j].val) { - ModInt t(a[j][j].val/a[i][j].val, mod); - for (int k = j; k < n; ++k) swap(a[i][k], a[j][k]-=t*a[i][k]); - for (int k = 0; k < n; ++k) swap(b[i][k], b[j][k]-=t*b[i][k]); - } - } - if (a[j][j].val == 0) return ModMatrix(0,0,0); - } - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - cout << a[i][j] << " "; + for (int i = 0, j, k; i < n; ++i) { + for (j = i; j < n && a[j][i].val == 0; ++j); + if (j == n) return ModMatrix(0,0,0); // regularity is checked by m = 0 + swap(a[i], a[j]); + swap(b[i], b[j]); + ModInt inv = a[i][i].inv(); + for (k = i; k < n; ++k) a[i][k] *= inv; + for (k = 0; k < n; ++k) b[i][k] *= inv; + for (j = 0; j < n; ++j) { + if (i == j || a[j][i].val == 0) continue; + ModInt c = a[j][i]; + for (k = i; k < n; ++k) a[j][k] -= c * a[i][k]; + for (k = 0; k < n; ++k) b[j][k] -= c * b[i][k]; } - cout << endl } - return B; } // It can be used for any composite modulo. From c02cf67e073e153f0bb8df24e0ea1d501c0bbb1d Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 28 Jan 2018 22:24:11 +0900 Subject: [PATCH 64/80] Modular Arithmetics --- math/modular_arithmetics.cc | 139 ++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 79 deletions(-) diff --git a/math/modular_arithmetics.cc b/math/modular_arithmetics.cc index ddd61c6..5110efe 100644 --- a/math/modular_arithmetics.cc +++ b/math/modular_arithmetics.cc @@ -43,9 +43,10 @@ ostream &operator<<(ostream &os, const vector> &v) { } using Int = long long; struct ModInt { - Int val, mod; - ModInt(Int v, Int m) : val(v), mod(m) { } - ModInt operator-() const { return ModInt(val?mod-val:val,mod); } + static Int mod; // Set this value + Int val; + ModInt(Int v=0) : val(v%mod) { } + ModInt operator-() const { return ModInt(val?mod-val:val); } ModInt &operator+=(ModInt a) { if ((val += a.val) >= mod) val -= mod; return *this; @@ -69,37 +70,38 @@ struct ModInt { val /= t; return (*this) *= a; } - ModInt inv() const { return ModInt(1,mod) /= (*this); } + ModInt inv() const { return ModInt(1) /= (*this); } bool operator<(ModInt x) const { return val < x.val; } }; -// ModInt modInt(Int v) { return ModInt(v,MOD); } +Int ModInt::mod = 1e9+7; + ostream &operator<<(ostream &os, ModInt a) { os << a.val; return os; } ModInt operator+(ModInt a, ModInt b) { return a += b; } ModInt operator-(ModInt a, ModInt b) { return a -= b; } ModInt operator*(ModInt a, ModInt b) { return a *= b; } ModInt operator/(ModInt a, ModInt b) { return a /= b; } ModInt pow(ModInt a, Int e) { - ModInt x(1, a.mod); + ModInt x(1); for (; e > 0; e /= 2) { if (e % 2 == 1) x *= a; a *= a; } return x; } -ModInt stringToModInt(string s, Int mod) { +ModInt stringToModInt(string s) { Int val = 0; for (int i = 0; i < s.size(); ++i) - val = (val*10 + (s[i]-'0')) % mod; - return ModInt(val, mod); + val = (val*10 + (s[i]-'0')) % ModInt::mod; + return ModInt(val); } - // compute inv[1], inv[2], ..., inv[mod-1] in O(n) time -vector inverse(Int mod) { - vector inv(mod, ModInt(0, mod)); +vector inverse() { + Int mod = ModInt::mod; + vector inv(mod); inv[1].val = 1; for (Int a = 2; a < mod; ++a) - inv[a] = inv[mod % a] * ModInt(mod - mod/a,mod); + inv[a] = inv[mod % a] * ModInt(mod - mod/a); return inv; } @@ -113,9 +115,10 @@ bool isQuadraticResidue(ModInt n) { return n.val == 0 || n.mod == 2 || pow(n, (n.mod-1)/2).val == 1; } ModInt sqrt(ModInt n) { - if (n.val == 0 || n.mod == 2) return n; - int M = __builtin_ctz(n.mod-1), Q = (n.mod-1)>>M; - ModInt z(2, n.mod); + const Int mod = ModInt::mod; + if (n.val == 0 || mod == 2) return n; + int M = __builtin_ctz(mod-1), Q = (mod-1)>>M; + ModInt z(2); while (isQuadraticResidue(z)) ++z.val; ModInt c = pow(z, Q); ModInt t = pow(n, Q); @@ -133,10 +136,10 @@ ModInt sqrt(ModInt n) { return R; } vector quadraticEquation(ModInt a, ModInt b, ModInt c) { - if (a.mod == 2) { + if (ModInt::mod == 2) { vector ans; if (c.val == 0) ans.push_back(c); - if ((a + b + c).val == 0) ans.push_back(ModInt(1,2)); + if ((a + b + c).val == 0) ans.push_back(ModInt(1)); return ans; } else { b /= (a+a); c /= a; @@ -150,13 +153,11 @@ vector quadraticEquation(ModInt a, ModInt b, ModInt c) { } // Discrete Logarithm by Shanks' Baby-Step Giant-Step -// // Find k such that a^k == b -// Int log(ModInt a, ModInt b) { - Int h = ceil(sqrt(a.mod+1e-9)); + Int h = ceil(sqrt(ModInt::mod+1e-9)); unordered_map hash; - ModInt x(1, a.mod); + ModInt x(1); for (Int i = 0; i < h; ++i) { if (!hash.count(x.val)) hash[x.val] = i; x *= a; @@ -170,41 +171,14 @@ Int log(ModInt a, ModInt b) { return -1; } - -// -// find solution z.val such that -// z.val == x.val (mod x.mod), for all x. -// the solution is unique in modulo z.mod. -// -Int extgcd(Int a, Int b, Int&x, Int&y) { - for (Int u = y = 1, v = x = 0; a; ) { - Int q = b / a; - swap(x -= q * u, u); - swap(y -= q * v, v); - swap(b -= q * a, a); - } - return b; // a x + b y == gcd(a, b) -} -ModInt chineseRemainder(vector modular) { - ModInt z(0, 1); // z == 0 (mod 1) - for (ModInt x: modular) { - Int u, v, g = extgcd(x.mod, z.mod, u, v); - z.val = z.val*u*x.mod + x.val*v*z.mod; - z.mod = z.mod * (x.mod / g); - } - if ((z.val %= z.mod) < 0) z.val += z.mod; - return z; -} - struct ModMatrix { int m, n; // m times n matrix vector> val; - Int mod; ModInt &operator()(int i, int j) { return val[i][j]; } - ModMatrix(int m, int n, Int mod) : - m(m), n(n), mod(mod), val(m, vector(n, ModInt(0,mod))) { } + ModMatrix(int m, int n) : + m(m), n(n), val(m, vector(n)) { } ModMatrix operator-() const { - ModMatrix A(m, n, mod); + ModMatrix A(m, n); for (int i = 0; i < m; ++i) for (int j = 0; j < n; ++j) A.val[i][j] = -val[i][j]; @@ -224,7 +198,7 @@ struct ModMatrix { } ModMatrix &operator*=(ModMatrix A) { for (int i = 0; i < m; ++i) { - vector row(A.n, ModInt(0, A.mod)); + vector row(A.n); for (int j = 0; j < A.n; ++j) { for (int k = 0; k < A.m; ++k) row[j] += val[i][k] * A.val[k][j]; @@ -233,22 +207,22 @@ struct ModMatrix { } return *this; } - static ModMatrix eye(int n, Int mod) { - ModMatrix I(n, n, mod); + static ModMatrix eye(int n) { + ModMatrix I(n, n); for (int i = 0; i < n; ++i) I.val[i][i].val = 1; return I; } - static ModMatrix zero(int n, Int mod) { - return ModMatrix(n, n, mod); + static ModMatrix zero(int n) { + return ModMatrix(n, n); } // mod should be prime ModMatrix inv() const { - ModMatrix B = eye(n, mod); + ModMatrix B = eye(n); vector> a = val; vector> &b = B.val; for (int i = 0, j, k; i < n; ++i) { for (j = i; j < n && a[j][i].val == 0; ++j); - if (j == n) return ModMatrix(0,0,0); // regularity is checked by m = 0 + if (j == n) return ModMatrix(0,0); // regularity is checked by m = 0 swap(a[i], a[j]); swap(b[i], b[j]); ModInt inv = a[i][i].inv(); @@ -266,12 +240,12 @@ struct ModMatrix { // It can be used for any composite modulo. ModInt det() const { vector> a = val; - ModInt D(1, mod); + ModInt D(1); for (int j = 0; j < n; ++j) { for (int i = j+1; i < n; ++i) { while (a[i][j].val) { D = -D; - ModInt t(a[j][j].val/a[i][j].val, mod); + ModInt t(a[j][j].val/a[i][j].val); for (int k = j; k < n; ++k) swap(a[i][k], a[j][k] -= t * a[i][k]); } @@ -285,7 +259,7 @@ ModMatrix operator+(ModMatrix A, ModMatrix B) { return A += B; } ModMatrix operator-(ModMatrix A, ModMatrix B) { return A -= B; } ModMatrix operator*(ModMatrix A, ModMatrix B) { return A *= B; } ModMatrix pow(ModMatrix A, int k) { - ModMatrix X = ModMatrix::eye(A.n, A.mod); + ModMatrix X = ModMatrix::eye(A.n); for (; k > 0; k /= 2) { if (k % 2 == 1) X *= A; A *= A; @@ -293,7 +267,7 @@ ModMatrix pow(ModMatrix A, int k) { return X; } ModInt dot(ModMatrix A, ModMatrix B) { - ModInt val(0, A.mod); + ModInt val; for (int i = 0; i < A.m; ++i) for (int j = 0; j < A.n; ++j) val += A.val[i][j] * B.val[i][j]; @@ -301,13 +275,18 @@ ModInt dot(ModMatrix A, ModMatrix B) { } using ModVector = vector; ModVector operator*(ModMatrix A, ModVector x) { - vector y(A.m, ModInt(0, A.mod)); + vector y(A.m); for (int i = 0; i < A.m; ++i) for (int j = 0; j < A.n; ++j) y[i] += A.val[i][j] * x[j]; return y; } - +ModInt dot(ModVector a, ModVector b) { + ModInt val; + for (int i = 0; i < a.size(); ++i) + val += a[i] * b[i]; + return val; +} // // Only available for prime modulos. @@ -318,8 +297,7 @@ struct LUDecomposition { int n; vector pi; vector> val; - Int mod; - LUDecomposition(ModMatrix A) : n(A.n), val(A.val), mod(A.mod) { + LUDecomposition(ModMatrix A) : n(A.n), val(A.val) { pi.resize(n+1); iota(all(pi), 0); for (int i = 0, j, k; i < n; ++i) { @@ -341,7 +319,7 @@ struct LUDecomposition { } bool isRegular() const { return pi[n] >= 0; } ModVector solve(ModVector b) { - vector x(b.size(), ModInt(0, mod)); + vector x(b.size()); for (int i = 0; i < n; ++i) { x[i] = b[pi[i]]; for (int k = 0; k < i; ++k) @@ -355,7 +333,7 @@ struct LUDecomposition { return x; } ModMatrix inverse() { // do not compute the inverse - ModMatrix B(n, n, mod); + ModMatrix B(n, n); for (int j = 0; j < n; ++j) { for (int i = 0; i < n; ++i) { if (pi[i] == j) B.val[i][j].val = 1; @@ -379,17 +357,17 @@ struct LUDecomposition { }; void mulTest() { - Int mod = 1e9+7; + ModInt::mod = 1e9+7; int m = 4, n = 4; - ModMatrix A(m,n,mod); - ModMatrix B(m,n,mod); - vector b(n, ModInt(0, mod)); + ModMatrix A(m,n); + ModMatrix B(m,n); + vector b(n); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { - A(i,j).val = rand() % mod; - B(i,j).val = rand() % mod; + A(i,j).val = rand() % ModInt::mod; + B(i,j).val = rand() % ModInt::mod; } - b[i].val = rand() % mod; + b[i].val = rand() % ModInt::mod; } ModMatrix C = A * B; @@ -415,8 +393,9 @@ void CF_QUADRATIC_EQUATIONS() { for (int icase = 0; icase < ncase; ++icase) { Int a, b, c, p; cin >> a >> b >> c >> p; + ModInt::mod = p; vector ans = quadraticEquation( - ModInt(a,p), ModInt(b,p), ModInt(c,p)); + ModInt(a), ModInt(b), ModInt(c)); cout << ans.size(); for (int i = 0; i < ans.size(); ++i) cout << " " << ans[i].val; @@ -425,7 +404,8 @@ void CF_QUADRATIC_EQUATIONS() { } void CF_DISCLOG() { - ModInt a(21309,999998999999), b(696969,999998999999); + ModInt::mod = 999998999999; + ModInt a(21309), b(696969); cout << log(a, b) << endl; } @@ -434,7 +414,8 @@ int SPOJ_MIFF() { int n, p; scanf("%d %d", &n, &p); if (n == 0 && p == 0) break; if (icase > 0) printf("\n"); - ModMatrix A(n, n, p); + ModInt::mod = p; + ModMatrix A(n, n); for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) scanf("%d", &A(i,j)); From 44755c46b0dc1b966a10357cb0ce1cd6dea10b35 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 30 Jan 2018 09:06:30 +0900 Subject: [PATCH 65/80] Delete modular_arithmetics.cc --- number_theory/modular_arithmetics.cc | 179 --------------------------- 1 file changed, 179 deletions(-) delete mode 100644 number_theory/modular_arithmetics.cc diff --git a/number_theory/modular_arithmetics.cc b/number_theory/modular_arithmetics.cc deleted file mode 100644 index 32e0ce8..0000000 --- a/number_theory/modular_arithmetics.cc +++ /dev/null @@ -1,179 +0,0 @@ -// -// Modular arithmetics (long long) -// -// Note: -// int < 2^31 < 10^9 -// long long < 2^63 < 10^18 -// -// feasible for M < 2^62 (10^18 < 2^62 < 10^19) -// -// -// Verified: -// SPOJ 11409: Fibonacci With a Twist -// SPOJ 9832: Matrix Inverse -// -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - - -#define All(c) c.begin(), c.end() -#define FOR(i,c) for(typeof(c.begin())i=c.begin();i!=c.end();++i) -#define REP(i,n) for(int i=0;i vec; -typedef vector mat; - -ll add(ll a, ll b, ll M) { - a += b; - if (a >= M) a -= M; - return a; -} -ll sub(ll a, ll b, ll M) { - if (a < b) a += M; - return a - b; -} - -// Correctness of mul -// ab = floor(ab / M) * M + (ab % M) -// -> (ab % M) = ab - floor(ab / M) * M -ll mul(ll a, ll b, ll M) { - ll r = a*b - floor(1.0*a*b/M)*M; - return r < 0 ? r + M : r >= M ? r - M : r; -} -ll pow(ll a, ll b, ll M) { - ll x = 1; - for (; b > 0; b >>= 1) { - if (b & 1) x = mul(x, a, M); - a = mul(a, a, M); - } - return x; -} -ll div(ll a, ll b, ll M) { - ll u = 1, x = 0, s = b, t = M; - while (s) { - ll q = t / s; - swap(x -= u * q, u); - swap(t -= s * q, s); - } - if (a % t) return -1; // infeasible - return mul(x < 0 ? x + M : x, a / t, M); -} - - -// Modular Matrix -mat eye(int n) { - mat I(n, vec(n)); - REP(i, n) I[i][i] = 1; - return I; -} -mat zeros(int n) { - return mat(n, vec(n)); -} -mat mul(mat A, mat B, ll M) { - int l = A.size(), m = B.size(), n = B[0].size(); - mat C(l, vec(n)); - REP(i,l) REP(k,m) REP(j,n) - C[i][j] = add(C[i][j], mul(A[i][k], B[k][j], M), M); - return C; -} -mat pow(mat A, ll b, ll M) { - mat X = eye(A.size()); - for (; b > 0; b >>= 1) { - if (b & 1) X = mul(X, A, M); - A = mul(A, A, M); - } - return X; -} -// assume: M is prime (singular ==> -// verify: SPOJ9832 -mat inv(mat A, ll M) { - int n = A.size(); - mat B(n, vec(n)); - for (int i = 0; i < n; ++i) - B[i][i] = 1; - - for (int i = 0; i < n; ++i) { - int j = i; - while (j < n && A[j][i] == 0) ++j; - if (j == n) return {}; - swap(A[i], A[j]); - swap(B[i], B[j]); - - ll inv = div(1, A[i][i], M); - for (int k = i; k < n; ++k) - A[i][k] = mul(A[i][k], inv, M); - for (int k = 0; k < n; ++k) - B[i][k] = mul(B[i][k], inv, M); - for (int j = 0; j < n; ++j) { - if (i == j || A[j][i] == 0) continue; - ll cor = A[j][i]; - for (int k = i; k < n; ++k) - A[j][k] = sub(A[j][k], mul(cor, A[i][k], M), M); - for (int k = 0; k < n; ++k) - B[j][k] = sub(B[j][k], mul(cor, B[i][k], M), M); - } - } - return B; -} - - - -void disp(mat A) { - cout << "["; - REP(i, A.size()) { - if (i != 0) cout << " "; - REP(j, A[i].size()) { - cout << A[i][j]; - if (j != A[i].size()-1) cout << ", "; - else cout << "; "; - } - cout << endl; - } - cout << endl; -} - - -ll binomial(ll n, ll k, ll M) { - ll num = 1, den = 1; - while (n > 0 || k > 0) { - ll m = n % M, l = k % M; - if (m < l) return 0; - if (l > m - l) l = m - l; - while (l > 0) { - num = mul(num, m--, M); - den = mul(den, l--, M); - } - n /= M; k /= M; - } - return div(num, den, M); -} - - -// tick a time -double tick() { - static clock_t oldtick; - clock_t newtick = clock(); - double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; - oldtick = newtick; - return diff; -} - - -const int N = 10000000; -ll x[N]; -int main() { -} From 0fbe8aa14277247440c0e999ae563129ed5486c0 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 30 Jan 2018 09:07:01 +0900 Subject: [PATCH 66/80] Modular Arithmetics --- {math => number_theory}/modular_arithmetics.cc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {math => number_theory}/modular_arithmetics.cc (100%) diff --git a/math/modular_arithmetics.cc b/number_theory/modular_arithmetics.cc similarity index 100% rename from math/modular_arithmetics.cc rename to number_theory/modular_arithmetics.cc From 8b15556b2c891466a961c555232be4a8200a0678 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Fri, 2 Feb 2018 04:28:37 +0900 Subject: [PATCH 67/80] Update gabow_edmonds.cc --- graph/gabow_edmonds.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/graph/gabow_edmonds.cc b/graph/gabow_edmonds.cc index 2c245b2..a0b5445 100644 --- a/graph/gabow_edmonds.cc +++ b/graph/gabow_edmonds.cc @@ -1,4 +1,6 @@ // +// !!It may incur the index-out-of-range error!! +// // General Graph Matching (Gabow-Edmonds) // // Description: From 7036d374a32a0797a84b8efb295943caf42bbace Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sat, 3 Feb 2018 09:07:55 -0600 Subject: [PATCH 68/80] Gabow's simplified version of Edmonds' Blossom Algorithm --- graph/gabow_edmonds.cc | 252 ++++++++++++++++++++++++++--------------- 1 file changed, 161 insertions(+), 91 deletions(-) diff --git a/graph/gabow_edmonds.cc b/graph/gabow_edmonds.cc index a0b5445..455b9a4 100644 --- a/graph/gabow_edmonds.cc +++ b/graph/gabow_edmonds.cc @@ -1,127 +1,197 @@ // -// !!It may incur the index-out-of-range error!! -// // General Graph Matching (Gabow-Edmonds) // // Description: -// It computes a maximum matching in a general graph. +// +// For a graph G = (V, E), a matching M is a set of edges +// such that any vertex is contained in M at most once. +// The matching with maximum cardinality is computed by +// the Edmonds blossom algorithm. +// +// This implementation is the Gabow's simplified version +// with the lazy update technique to improve the complexity +// in sparse graphs. +// +// +// Complexity: +// +// O(n m log n) // -// Algorithm: -// Gabow's simplified version of Edmonds' blossom algorithm. -// -// Comlexity: -// O(n^3) // // Verified: -// LA3820, LA4130 +// +// SPOJ ADABLOOM +// // // References: // H.Gabow (1976): // An efficient implementation of Edmonds' algorithm for maximum matching on graphs. // Journal of the ACM, vol.23, no.2, pp.221-234. // +// http://min-25.hatenablog.com/entry/2016/11/21/222625 +// +// -#include -#include -#include -#include -#include -#include -#include -#include - +// g++ -std=c++17 -O3 -fmax-errors=1 -fsanitize=undefined +#include using namespace std; -struct graph { +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +struct Graph { int n; - vector> adj; - graph(int n) : n(n), adj(n) { }; - void add_edge(int x, int y) { - adj[x].push_back(y); - adj[y].push_back(x); + vector< vector > adj; + Graph(int n) : n(n), adj(n) { }; + void addEdge(int u, int v) { + adj[u].push_back(v); + adj[v].push_back(u); } - queue Q; - vector label, mate, cycle; - void rematch(int x, int y) { - int m = mate[x]; mate[x] = y; - if (mate[m] == x) { - if (label[x] < n) { - rematch(mate[m] = label[x], m); + + vector mate; + int maximumMatching() { + mate.assign(n+1, n); + vector first(n+1, n), que(n); + vector> label(n+1, make_pair(-1,-1)); + int head = 0, tail = 0; + + function rematch = [&](int v, int w) { + int t = mate[v]; mate[v] = w; + if (mate[t] != v) return; + if (label[v].snd == -1) { + mate[t] = label[v].fst; + rematch(mate[t], t); } else { - int s = (label[x]-n)/n, t = (label[x]-n)%n; - rematch(s, t); rematch(t, s); + int x, y; tie(x, y) = label[v]; + rematch(x, y); rematch(y, x); } - } - } - void traverse(int x) { - vector save = mate; - rematch(x, x); - for (int u = 0; u < n; ++u) - if (mate[u] != save[u]) cycle[u] ^= 1; - save.swap(mate); - } - void relabel(int x, int y) { - cycle = vector(n, 0); - traverse(x); - traverse(y); - for (int u = 0; u < n; ++u) { - if (!cycle[u] || label[u] >= 0) continue; - label[u] = n+x+y*n; - Q.push(u); - } - } - int augment(int r) { - label.assign(n, -2); - label[r] = -1; - Q = queue(); Q.push(r); - while (!Q.empty()) { - int x = Q.front(); Q.pop(); - for (int y: adj[x]) { - if (mate[y] < 0 && r != y) { - rematch(mate[y] = x, y); return 1; - } else if (label[y] >= -1) { - relabel(x, y); - } else if (label[mate[y]] < -1) { - label[mate[y]] = x; - Q.push(mate[y]); + }; + auto relabel = [&](int x, int y) { + function findFirst = [&](int u) { + return label[first[u]].fst < 0 ? first[u] : + first[u] = findFirst(first[u]); + }; + int r = findFirst(x), s = findFirst(y); + if (r == s) return; + auto h = make_pair(~x, y); + label[r] = label[s] = h; + int join; + while (1) { + if (s != n) swap(r, s); + r = findFirst(label[mate[r]].fst); + if (label[r] == h) { + join = r; + break; + } else { + label[r] = h; } } - } - return 0; - } - int maximum_matching() { - mate.assign(n, -2); + for (int v: {first[x], first[y]}) { + for (; v != join; v = first[label[mate[v]].fst]) { + label[v] = make_pair(x, y); + first[v] = join; + que[tail++] = v; + } + } + }; + auto augment = [&](int u) { + label[u] = make_pair(n, -1); + first[u] = n; + head = tail = 0; + for (que[tail++] = u; head < tail;) { + int x = que[head++]; + for (int y: adj[x]) { + if (mate[y] == n && y != u) { + mate[y] = x; + rematch(x, y); + return true; + } else if (label[y].fst >= 0) { + relabel(x, y); + } else if (label[mate[y]].fst == -1) { + label[mate[y]].fst = x; + first[mate[y]] = y; + que[tail++] = mate[y]; + } + } + } + return false; + }; int matching = 0; - for (int u = 0; u < n; ++u) - if (mate[u] < 0) matching += augment(u); + for (int u = 0; u < n; ++u) { + if (mate[u] < n || !augment(u)) continue; + ++matching; + for (int i = 0; i < tail; ++i) + label[que[i]] = label[mate[que[i]]] = make_pair(-1,-1); + label[n] = make_pair(-1, -1); + } return matching; } }; -int doit() { - int n, m; - scanf("%d %d", &n, &m); - vector v(n); - for (int i = 0; i < n; ++i) { - scanf("%d", &v[i]); +void LA3820() { + int ncase; scanf("%d", &ncase); + for (int icase = 0; icase < ncase; ++icase) { + int n, m; + scanf("%d %d", &n, &m); + vector v(n); + for (int i = 0; i < n; ++i) { + scanf("%d", &v[i]); + } + set S; + for (int i = 0; i < m; ++i) { + int x; + scanf("%d", &x); + S.insert(x); + } + Graph G(n); + for (int i = 0; i < n; ++i) { + for (int j = i+1; j < n; ++j) { + if (S.count(v[i] + v[j])) + G.addEdge(i, j); + } + } + printf("%d\n", G.maximumMatching()); } - set S; +} + +void UOJ79() { + int n, m; cin >> n >> m; + Graph g(n); for (int i = 0; i < m; ++i) { - int x; - scanf("%d", &x); - S.insert(x); + int u, v; + cin >> u >> v; + g.addEdge(u-1, v-1); } - - graph g(n); - for (int i = 0; i < n; ++i) { - for (int j = i+1; j < n; ++j) { - if (S.count(v[i] + v[j])) - g.add_edge(i, j); + cout << g.maximumMatching() << endl; + for (int u = 0; u < n; ++u) { + if (u > 0) cout << " "; + if (g.mate[u] >= n) cout << 0; + else cout << g.mate[u]+1; + } + cout << endl; +} +void SPOJ_ADABLOOM() { + int ncase; scanf("%d", &ncase); + for (int icase; icase < ncase; ++icase) { + int n; scanf("%d", &n); + vector a(n); + for (int i = 0; i < n; ++i) + scanf("%lld", &a[i]); + random_shuffle(all(a)); + Graph g(n); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + if (a[i] < (a[i] ^ a[j]) && (a[i] ^ a[j]) < a[j]) g.addEdge(i, j); + } } + cout << g.maximumMatching() << endl; } - return g.maximum_matching(); } + int main() { - doit(); + SPOJ_ADABLOOM(); + //UOJ79(); + //LA3820(); } From 400f2f633d18508288a45307d32d258188dfb38b Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 4 Feb 2018 04:23:02 +0900 Subject: [PATCH 69/80] Hopcroft-Tarjan's Articulation Point / Biconnected Components --- graph/articulation_points.cc | 147 +++++++++++++++++++++-------------- 1 file changed, 90 insertions(+), 57 deletions(-) diff --git a/graph/articulation_points.cc b/graph/articulation_points.cc index 07c5dd0..62b6638 100644 --- a/graph/articulation_points.cc +++ b/graph/articulation_points.cc @@ -1,5 +1,5 @@ // -// Articulation points / Biconnected components +// Block-Cut Tree (Articulation points / Biconnected components) // // Description: // Let G = (V, E). If G-v is disconnected, v in V is said to @@ -7,25 +7,19 @@ // it is said to be biconnected. // // A biconnected component is a maximal biconnected subgraph. -// The algorithm finds all articulation points and biconnected -// components. +// The algorithm finds all articulation points and biconnected +// components. It can be obtained by the Hopcroft-Tarjan DFS. // -// The most important fact is that by contracting biconnected -// components we obtain a tree, which is called the block tree. -// -// -// Algorithm: -// Hopcroft-Tarjan's DFS based algorithm. -// -// Single DFS finds a block tree rooted from the component -// that contains the specified root. +// We maintain the biconnected component decomposition by the +// block tree whose vertices are the blocks and the articulation +// points. By contracting the graph by the articulation points, +// we obtain the intersection graph of the blocks. // // Complexity: // O(n + m). // // Verified: -// SPOJ 14956: Submerging Island (articulation point) -// POJ 2942: Knights of the Round Table (biconnected components) +// AOJ_GRL_3_A (articulation point) // // References: // J. Hopcroft and R. E. Tarjan (1973): @@ -33,68 +27,107 @@ // Communications of the ACM, vol.16, no.6, pp.372-378. // -#include -#include -#include -#include -#include -#include +// g++ -std=c++17 -O3 -fmax-errors=1 -fsanitize=undefined +#include using namespace std; #define fst first #define snd second #define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } -struct graph { +struct Graph { int n; vector> adj; - graph(int n) : n(n), adj(n) { } - void add_edge(int src, int dst) { - adj[src].push_back(dst); - adj[dst].push_back(src); + Graph(int n) : n(n), adj(n) { } + void addEdge(int u, int v) { + adj[u].push_back(v); + adj[v].push_back(u); } +}; - void biconnected_components() { - vector num(n), low(n), S; - unordered_set arts; +// +// bcc.n-bcc.block.size() is the number of articulation points +// index[u] is the corresponding node in the block-cut tree. +// Here, if u in block[k] then +// if u is an articulation point, k in adj[index[u]] +// otherwise, index[u] = k +// +struct BiconnectedComponents : Graph { + vector is_articulation, index; + vector> block; - function dfs = [&](int p, int u, int &t) { - num[u] = low[u] = ++t; - S.push_back(u); - for (int v: adj[u]) { - if (v == p) continue; - if (num[v] == 0) { - dfs(u, v, t); + BiconnectedComponents(Graph g) : Graph(0) { + vector low(g.n), num(g.n), cur(g.n), par(g.n, -1), path; + is_articulation.resize(g.n); + for (int s = 0; s < g.n; ++s) { + if (num[s]) continue; + int time = 0; + vector stack = {s}; + while (!stack.empty()) { + int u = stack.back(); + if (cur[u] == 0) { + low[u] = num[u] = ++time; + path.push_back(u); + } + if (cur[u] == g.adj[u].size()) { + stack.pop_back(); + } else if (cur[u] >= 0) { + int v = g.adj[u][cur[u]++]; + if (num[v] == 0) { + cur[u] = ~cur[u]; + stack.push_back(v); + } else if (v != par[u]) { + low[u] = min(low[u], num[v]); + } + } else { + cur[u] = ~cur[u]; + int v = g.adj[u][cur[u]-1]; low[u] = min(low[u], low[v]); if (num[u] <= low[v]) { - if (num[u] != 1 || num[v] > 2) { - // here, u is an articulation point if - // (a). u is non-root - // (b). u is root with two more children - } - vector C = {u}; // biconnected component - while (C.back() != v) { - C.push_back(S.back()); - S.pop_back(); + is_articulation[u] = (num[u] > 1 || num[v] > 2); + block.push_back({u}); + while (block.back().back() != v) { + block.back().push_back(path.back()); + path.pop_back(); } } - } else low[u] = min(low[u], num[v]); + } } - }; - for (int u = 0, t; u < n; ++u) - if (!num[u]) dfs(-1, u, t = 0); - cout << arts.size() << endl; + } + index.resize(g.n); // make a block tree + n = block.size(); + for (int u = 0; u < g.n; ++u) + if (is_articulation[u]) index[u] = n++; + adj.resize(n); + for (int k = 0; k < block.size(); ++k) { + for (int u: block[k]) { + if (!is_articulation[u]) index[u] = k; + else addEdge(k, index[u]); + } + } } }; -int main() { - for (int n, m; ~scanf("%d %d", &n, &m) && n; ) { - graph g(n); - for (int i = 0; i < m; ++i) { - int u, v; scanf("%d %d", &u, &v); - g.add_edge(u-1, v-1); - } - g.biconnected_components(); +void AOJ_GRL_3_A() { + int n, m; + scanf("%d %d", &n, &m); + Graph g(n); + for (int i = 0; i < m; ++i) { + int u, v; scanf("%d %d", &u, &v); + g.addEdge(u, v); } + BiconnectedComponents bcc(g); + for (int u = 0; u < g.n; ++u) + if (bcc.is_articulation[u]) cout << u << endl; } + +int main() { + AOJ_GRL_3_A(); + //SPOJ_SUBMERGE(); + //test(); + /* + */ +} + From 8b1daaa3e80e5d954aac2632ef2299b1b86c55ed Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Mon, 5 Feb 2018 14:36:02 -0600 Subject: [PATCH 70/80] Kahn's topological sort --- graph/topological_sort.cc | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 graph/topological_sort.cc diff --git a/graph/topological_sort.cc b/graph/topological_sort.cc new file mode 100644 index 0000000..66cfd16 --- /dev/null +++ b/graph/topological_sort.cc @@ -0,0 +1,81 @@ +// +// Topological Sort +// +// +// Description: +// +// Let G = (V, E) be a graph. An ordering ord: [n] -> V is a topological +// ordering if i > j then there is no edge from ord[i] to ord[j]. +// G has a topological ordering if and only if G is DAG. +// +// A topological order can be obtained in O(n + m) time by using +// an iterative method (Kahn's algorithm) or a recursive method +// (by Tarjan's algorithm). The following implementation is a +// Kahn's algorithm. +// +// Note that if you want to find the all topological orders, +// +// +// Complexity: +// +// O(n + m) +// +// +// Verified: +// +// AOJ GPL_4_B +// +// References: +// +// Arthur B. Kahn (1962): +// "Topological sorting of large networks". +// Communications of the ACM, 5 (11): 558--562. +// +#include +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +struct Graph { + int n; + vector> adj; + Graph(int n) : n(n), adj(n) { } + void addEdge(int u, int v) { + adj[u].push_back(v); + } +}; + +// return empty list if g has no topological order +vector topologicalSort(Graph g) { + vector deg(g.n); + for (int u = 0; u < g.n; ++u) + for (int v: g.adj[u]) ++deg[v]; + vector stack; + for (int u = 0; u < g.n; ++u) + if (!deg[u]) stack.push_back(u); + + vector order; + while (!stack.empty()) { + int u = stack.back(); stack.pop_back(); + order.push_back(u); + for (int v: g.adj[u]) + if (!--deg[v]) stack.push_back(v); + } + return order.size() == g.n ? order : vector(); +} + +int main() { + int n, m; cin >> n >> m; + Graph g(n); + for (int i = 0; i < m; ++i) { + int u, v; cin >> u >> v; + g.addEdge(u, v); + } + auto ord = topologicalSort(g); + for (int i = 0; i < ord.size(); ++i) { + if (i > 0) cout << " "; + cout << ord[i]; + } +} From bf3828854ed0c249deb4f494ec1daf865dd1e41d Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 6 Feb 2018 09:23:53 -0600 Subject: [PATCH 71/80] Bridge-Block Tree (Bridge / Two-edge connected components) --- graph/bridge.cc | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 graph/bridge.cc diff --git a/graph/bridge.cc b/graph/bridge.cc new file mode 100644 index 0000000..de6cdba --- /dev/null +++ b/graph/bridge.cc @@ -0,0 +1,149 @@ +// +// Bridge-Block Tree (Bridge / Two-edge connected component) +// +// Description: +// Let G = (V, E). e in E is said to be a cut edge if G-e is +// disconnected If G has no cut edges, it is said to be two-edge +// connected. +// A two-edge connected component is a maximal two-edge connected +// subgraph. The algorithm finds all bridges with the two-edge +// connected components. +// +// We maintain the two-edge connected component decomposition by +// a bridge-block tree whose nodes are the two-edge connected +// components. +// +// +// Complexity: +// O(n + m). +// +// Verified: +// +// References: +// + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) + +struct Graph { + int n; + vector> adj; + Graph(int n) : n(n), adj(n) { } + void addEdge(int src, int dst) { + adj[src].push_back(dst); + adj[dst].push_back(src); + } +}; + +struct BridgeBlockTree : Graph { + vector index; // index[u] is the block index containing u + vector> block; // u in block[k] <=> index[u] == k + + BridgeBlockTree(Graph g) : Graph(0) { + index.assign(g.n, -1); + vector num(g.n), par(g.n,-1), cur(g.n); + + for (int s = 0; s < g.n; ++s) { + if (num[s]) continue; + int time = 0; + vector snum, path, stack = {s}; + while (!stack.empty()) { + int u = stack.back(); + if (cur[u] == 0) { + num[u] = ++time; + path.push_back(u); + snum.push_back(num[u]); + } + if (cur[u] == g.adj[u].size()) { + if (num[u] == snum.back()) { + snum.pop_back(); + block.push_back({}); + while (1) { + int w = path.back(); path.pop_back(); + block.back().push_back(w); + index[w] = block.size()-1; + if (u == w) break; + } + } + stack.pop_back(); + } else { + int v = g.adj[u][cur[u]++]; + if (!num[v]) { + par[v] = u; + stack.push_back(v); + } else if (v != par[u] && index[v] < 0) { + while (snum.back() > num[v]) snum.pop_back(); + } + } + } + } + n = block.size(); + adj.resize(n); + for (int u = 0; u < g.n; ++u) + if (par[u] >= 0 && index[u] != index[par[u]]) + addEdge(index[u], index[par[u]]); + } +}; + + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} + +int main() { + Graph g(6); + g.addEdge(0, 1); + g.addEdge(1, 2); + g.addEdge(2, 0); + g.addEdge(2, 3); + g.addEdge(3, 4); + g.addEdge(4, 5); + g.addEdge(5, 3); + BridgeBlockTree t(g); + + cout << t.n << endl; + for (int u = 0; u < t.n; ++u) { + for (int v: t.adj[u]) { + cout << u << " " << v << endl; + } + } + + for (auto B: t.block) { + for (int u: B) { + cout << u << " "; + } + cout << endl; + } + for (int u = 0; u < 6; ++u) { + cout << t.index[u] << " "; + } + cout << endl; + + //g.bridgeless_component(); + /* + for (int n, m; ~scanf("%d %d", &n, &m) && n; ) { + graph g(n); + for (int i = 0; i < m; ++i) { + int u, v; scanf("%d %d", &u, &v); + g.add_edge(u-1, v-1); + } + g.biconnected_components(); + } + */ +} From 40b1a4821e4bb1ebd49c4b5d53e3de719d8f55ea Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 6 Feb 2018 11:29:47 -0600 Subject: [PATCH 72/80] Number of lattice points below a line --- math/lattice_below_line.cc | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 math/lattice_below_line.cc diff --git a/math/lattice_below_line.cc b/math/lattice_below_line.cc new file mode 100644 index 0000000..893859f --- /dev/null +++ b/math/lattice_below_line.cc @@ -0,0 +1,71 @@ +// +// Number of lattice points below a line +// +// Description: +// +// Let a, b, n, m be nonnegative integers. The task is to compute +// sum_{i in [0,n)} floor((a + ib)/m). +// +// We compute this quantity in x-axis and y-axis alternately. +// First, let +// a = (a/m) m + (a%m), +// b = (b/m) m + (b%m). +// Then the quantity is +// sum [(a/m)+i*(b/m)] + floor(((a%m) + i(b%m))/m) +// Here, the first term is analytically evaluated. +// If b%m == 0 then the second term is zero. +// Otherwise, the task is reduced to compute +// sum_{i in [0,n)} floor((a + ib)/m) +// where a < m, b < m. By changing the axes, we can observe +// that this quantity is equal to +// sum_{i in [0,n')} floor((a' + ib')/m') +// where +// n' = (a + b n) / m, +// a' = (a + b n) % m, +// b' = m, +// m' = b. +// +// We can observe that the computation on b and m is the same +// as the computation of gcd(b,m). Thus the number of iterations +// is at most O(log m). +// +// Complexity: +// +// O(log m). + +#include +#include +#include +#include +#include + +using namespace std; + +#define fst first +#define snd second +#define aInt(c) ((c).begin()), ((c).end()) + +// +// sum_{0<=i= 0, a >= 0, b >= 0 +// +// +using Int = long long; +Int latticeBelowLine(Int n, Int a, Int b, Int m) { + Int ans = 0; + while (m) { + ans += (n-1)*n/2*(b/m) + n*(a/m); + a %= m; + b %= m; + auto z = (a+b*n); + a = z%m; + n = z/m; + swap(b, m); + } + return ans; +} + +int main() { + srand(time(0)); + Int a = rand(), b = rand(), n = rand(), m = rand(); + cout << latticeBelowLine(n, a, b, m) << endl; +} From 53b8fe26c74d6b9b6456c96759ff9bc785d32813 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Tue, 6 Feb 2018 11:41:59 -0600 Subject: [PATCH 73/80] Number of lattice points below a line --- math/lattice_below_line.cc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/math/lattice_below_line.cc b/math/lattice_below_line.cc index 893859f..e9ee769 100644 --- a/math/lattice_below_line.cc +++ b/math/lattice_below_line.cc @@ -6,18 +6,17 @@ // Let a, b, n, m be nonnegative integers. The task is to compute // sum_{i in [0,n)} floor((a + ib)/m). // -// We compute this quantity in x-axis and y-axis alternately. +// We compute this quantity in two directions alternately. // First, let // a = (a/m) m + (a%m), // b = (b/m) m + (b%m). // Then the quantity is // sum [(a/m)+i*(b/m)] + floor(((a%m) + i(b%m))/m) // Here, the first term is analytically evaluated. -// If b%m == 0 then the second term is zero. -// Otherwise, the task is reduced to compute +// The second term is zero if b%m == 0. Otherwise, the task is +// reduced to compute // sum_{i in [0,n)} floor((a + ib)/m) -// where a < m, b < m. By changing the axes, we can observe -// that this quantity is equal to +// where a < m, b < m. By changing the axes, this quantity is // sum_{i in [0,n')} floor((a' + ib')/m') // where // n' = (a + b n) / m, @@ -25,13 +24,17 @@ // b' = m, // m' = b. // -// We can observe that the computation on b and m is the same -// as the computation of gcd(b,m). Thus the number of iterations -// is at most O(log m). +// We evaluate the number of iterations. Since the computation +// between b and m is the same as the one of the Euclidean +// algorithm. Thus it terminates in O(log m) time. // // Complexity: // // O(log m). +// +// Verified: +// +// Somewhere #include #include @@ -43,7 +46,7 @@ using namespace std; #define fst first #define snd second -#define aInt(c) ((c).begin()), ((c).end()) +#define all(c) ((c).begin()), ((c).end()) // // sum_{0<=i= 0, a >= 0, b >= 0 From d3415627fd1ccd7a2f946fcc6ad9c546188617fd Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Wed, 7 Feb 2018 00:48:02 -0600 Subject: [PATCH 74/80] Leftist heap --- data_structure/leftist_heap.cc | 84 ++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 data_structure/leftist_heap.cc diff --git a/data_structure/leftist_heap.cc b/data_structure/leftist_heap.cc new file mode 100644 index 0000000..62e3609 --- /dev/null +++ b/data_structure/leftist_heap.cc @@ -0,0 +1,84 @@ +// +// Leftist Heap +// +// Description: +// +// Leftist heap is a heap data structure that allows +// the meld (merge) operation in O(log n) time. +// Use this for persistent heaps. +// +// Complexity: +// +// O(1) for top, O(log n) for push/pop/meld +// +// g++ -std=c++17 -O3 -fmax-errors=1 -fsanitize=undefined +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +template +struct LeftistHeap { + struct Node { + T key; + Node *left = 0, *right = 0; + int dist = 0; + } *root = 0; + static Node *merge(Node *x, Node *y) { + if (!x) return y; + if (!y) return x; + if (x->key > y->key) swap(x, y); + x->right = merge(x->right, y); + if (!x->left || x->left->dist < x->dist) swap(x->left, x->right); + x->dist = (x->right ? x->right->dist : 0) + 1; + return x; + } + void push(T key) { root = merge(root, new Node({key})); } + void pop() { root = merge(root->left, root->right); } + T top() { return root->key; } +}; + +// +// Persistent Implementaiton. (allow copy) +// +template +struct PersistentLeftistHeap { + struct Node { + T key; + Node *left = 0, *right = 0; + int dist = 0; + } *root = 0; + static Node *merge(Node *x, Node *y) { + if (!x) return y; + if (!y) return x; + if (x->key > y->key) swap(x, y); + x = new Node(*x); + x->right = merge(x->right, y); + if (!x->left || x->left->dist < x->dist) swap(x->left, x->right); + x->dist = (x->right ? x->right->dist : 0) + 1; + return x; + } + void push(T key) { root = merge(root, new Node({key})); } + void pop() { root = merge(root->left, root->right); } + T top() { return root->key; } +}; + +int main() { + PersistentLeftistHeap heap; + heap.push(3); + heap.push(1); + heap.push(4); + heap.push(1); + heap.push(5); + cout << heap.top() << endl; heap.pop(); + cout << heap.top() << endl; heap.pop(); + auto temp = heap; + cout << heap.top() << endl; heap.pop(); + cout << heap.top() << endl; heap.pop(); + cout << temp.top() << endl; temp.pop(); + cout << temp.top() << endl; temp.pop(); +} From 516f777c2431af75e50e1dae36d0e8bfced3ba6d Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Sun, 11 Feb 2018 13:46:24 +0900 Subject: [PATCH 75/80] Fisher, Kasteleyn, and Temperley algorithm for counting perfect matchings in plane graph --- graph/plane_perfect_matchings.cc | 196 +++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 graph/plane_perfect_matchings.cc diff --git a/graph/plane_perfect_matchings.cc b/graph/plane_perfect_matchings.cc new file mode 100644 index 0000000..095d48a --- /dev/null +++ b/graph/plane_perfect_matchings.cc @@ -0,0 +1,196 @@ +// +// Counting Perfect Matchings in Plane Graph +// (Fisher, Kasteleyn, and Temperley) +// +// Description: +// +// Pfaffian Orientation; see https://en.wikipedia.org/wiki/FKT_algorithm +// +// Complexity: +// +// O(n^3). +// +// g++ -std=c++17 -O3 -fmax-errors=1 -fsanitize=undefined +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +using Real = long double; +struct Point { + Real x, y; +}; + +struct PlaneGraph { + vector incident_edge; // vertex record + vector origin, twin, prev, next, incident_face; // edge record + vector component; // face record + int edges() const { return origin.size(); } + int vertices() const { return incident_edge.size(); } + int faces() const { return component.size(); } + + vector point; + int newVertex(Point p, int e = -1) { + point.push_back(p); + incident_edge.push_back(e); + return vertices()-1; + } + int newEdge(int o = -1) { + origin.push_back(o); + twin.push_back(-1); + prev.push_back(-1); + next.push_back(-1); + incident_face.push_back(-1); + return edges()-1; + } + int newFace(int e = -1) { + component.push_back(e); + return component.size()-1; + } + void completeFaces() { + component.clear(); + fill(all(incident_face), -1); + for (int e = 0; e < edges(); ++e) { + if (incident_face[e] >= 0) continue; + int f = newFace(e), x = e; + do { + incident_face[x] = f; + x = next[x]; + } while (x != e); + } + } + + // assume connected + vector pfaffianOrientation() { + // take any spanning tree T + vector dir(edges(), -2), seen(vertices()); + function dfs1 = [&](int u) { + seen[u] = 1; + int e = incident_edge[u]; + do { + int v = origin[twin[e]]; + if (!seen[v]) { + dir[e] = 1; + dir[twin[e]] = -dir[e]; + dfs1(v); + } + e = next[twin[e]]; + } while (e != incident_edge[u]); + }; + for (int u = 0; u < vertices(); ++u) + if (!seen[u]) dfs1(u); + + // take any dual spanning tree that does not cross T + seen = vector(faces()); + vector come(faces(), -1); + function dfs2 = [&](int f, int p) { + int parity = 0; + int free_edge = -1; + seen[f] = 1; + int e = component[f]; + do { + int g = incident_face[twin[e]]; + if (dir[e] == -2 && !seen[g]) dfs2(g, twin[e]); + if (dir[e] == -2) { assert(free_edge == -1); free_edge = e; } + else if (dir[e] == 1) ++parity; + e = next[e]; + } while (e != component[f]); + if (free_edge != -1) { + dir[free_edge] = -(parity % 2 == 0 ? -1 : +1); + dir[twin[free_edge]] = -dir[free_edge]; + } + }; + dfs2(0, -1); + return dir; + } + using Int = __int128_t; + Int countPerfectMatching() { + vector dir = pfaffianOrientation(); + int n = vertices(); + vector> A(n, vector(n)); + for (int e = 0; e < edges(); ++e) + A[origin[e]][origin[twin[e]]] = dir[e]; + + // compute determinant + Int det = 1; + for (int j = 0; j < n; ++j) { + for (int i = j+1; i < n; ++i) { + while (A[i][j]) { + det = -det; + Int t = A[j][j] / A[i][j]; + for (int k = j; k < n; ++k) + swap(A[i][k], A[j][k] -= t * A[i][k]); + } + } + det *= A[j][j]; // % mod + } + return sqrt(det + 0.1); + } +}; + +using Int = long long; +Int dominoCount(vector> table) { + int m = table.size(), n = table[0].size(); + vector> index(m, vector(n, -1)); + PlaneGraph g; + unordered_map> adj; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (table[i][j] == '.') { + index[i][j] = g.newVertex(Point({i,j})); + } + } + } + unordered_map next_inc, prev_inc; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + vector inc; + int x = index[i][j]; + int dx[] = {1,0,-1,0}, dy[] = {0,1,0,-1}; + for (int p = 0; p < 4; ++p) { + int k = i+dx[p], l = j+dy[p]; + if (k < 0 || l < 0) continue; + if (k >= table.size() || l >= table[k].size()) continue; + if (table[i][j] != '.' || table[k][l] != '.') continue; + int y = index[k][l]; + if (!adj[x].count(y)) adj[x][y] = g.newEdge(x); + if (!adj[y].count(x)) adj[y][x] = g.newEdge(y); + g.twin[adj[x][y]] = adj[y][x]; + g.twin[adj[y][x]] = adj[x][y]; + g.incident_edge[x] = adj[x][y]; + g.incident_edge[y] = adj[y][x]; + inc.push_back(adj[x][y]); + } + for (int i = 0; i < inc.size(); ++i) { + int j = (i == inc.size()-1 ? 0 : i+1); + next_inc[inc[i]] = inc[j]; + prev_inc[inc[j]] = inc[i]; + } + } + } + for (int e = 0; e < g.edges(); ++e) { + g.next[e] = prev_inc[g.twin[e]]; + g.prev[e] = g.twin[next_inc[e]]; + } + g.completeFaces(); + return g.countPerfectMatching(); +} + +void SPOJ_GNY07H() { + int ncase; + cin >> ncase; + for (int icase = 0; icase < ncase; ++icase) { + int w; + cin >> w; + vector> table(4, vector(w, '.')) ; + cout << icase+1 << " " << dominoCount(table) << endl; + } +} + +int main() { + SPOJ_GNY07H(); +} From 02236d75dc048a421a22fdf2b12ad71e26ca4180 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Mon, 12 Feb 2018 15:04:37 +0900 Subject: [PATCH 76/80] Eppstein's k shortest walks --- graph/k_shortest_walks.cc | 181 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 graph/k_shortest_walks.cc diff --git a/graph/k_shortest_walks.cc b/graph/k_shortest_walks.cc new file mode 100644 index 0000000..d37c038 --- /dev/null +++ b/graph/k_shortest_walks.cc @@ -0,0 +1,181 @@ +// +// K Shortest Walks (Simplified Eppstein) +// +// Description +// +// We are given a weighted graph. The k-shortest walks problem +// seeks k different s-t walks (paths allowing repeated vertices) +// in the increasing order of the lengths. +// +// If we maintain each walks explicitly, it must costs O(k^2 m) time. +// To avoid this complexity, we maintain the walks in a compact format. +// Let us fix a reverse shortest path tree from t. A deviation is an +// edge that is not on the tree. Any walk is represented by a concatenation +// of deviations and paths on the tree. We enumerate all possible +// deviations and use the best-first search to find the k-th solution. +// +// The Eppstein's algorithm maintains the set of deviations by +// the augmented persistent heaps and emurates the best-first search. +// Here, we implemented a simplified version of the Eppstein's algorithm, +// which uses the simple persistent heaps instead of the augmented ones. +// It increases the space from O(m + n log n) to O(m log n). +// +// Complexity: +// +// O(m log m) construction +// O(k log k) for k-th search +// +// Verified: +// +// UTPC2013_10 J K-th Cycle +// +// References: +// +// David Eppstein (1998): +// "Finding the k shortest paths", +// SIAM Journal on computing, vol.28, no.2, pp.652--673. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +struct Graph { + int n, m = 0; + vector head; // Vertex + vector src, dst, next, prev; // Edge + + using Weight = long long; + vector weight; + Graph(int n) : n(n), head(n, -1) { } + int addEdge(int u, int v, Weight w) { + next.push_back(head[u]); + src.push_back(u); + dst.push_back(v); + weight.push_back(w); + return head[u] = m++; + } +}; +constexpr Graph::Weight INF = 1e15; + +struct KShortestWalks { + Graph g; + vector dist; + vector tree, order; + void reverseDijkstra(int t) { + vector> adj(g.n); + for (int u = 0; u < g.n; ++u) + for (int e = g.head[u]; e >= 0; e = g.next[e]) + adj[g.dst[e]].push_back(e); + dist.assign(g.n, INF); + tree.assign(g.n, ~g.m); + using Node = tuple; + priority_queue, greater> que; + que.push(make_tuple(0, t)); + dist[t] = 0; + while (!que.empty()) { + int u = get<1>(que.top()); que.pop(); + if (tree[u] >= 0) continue; + tree[u] = ~tree[u]; + order.push_back(u); + for (int e: adj[u]) { + int v = g.src[e]; + if (dist[v] > dist[u] + g.weight[e]) { + tree[v] = ~e; + dist[v] = dist[u] + g.weight[e]; + que.push(Node(dist[v], v)); + } + } + } + } + struct Node { // Persistent Heap (Leftist Heap) + int e; + Graph::Weight delta; + Node *left = 0, *right = 0; + int rnk = 0; + } *root = 0; + static Node *merge(Node *x, Node *y) { + if (!x) return y; + if (!y) return x; + if (x->delta > y->delta) swap(x, y); + x = new Node(*x); + x->right = merge(x->right, y); + if (!x->left || x->left->rnk < x->rnk) swap(x->left, x->right); + x->rnk = (x->right ? x->right->rnk : 0) + 1; + return x; + } + vector deviation; + void buildHeap() { + deviation.resize(g.n); + for (int u: order) { + int v = -1; + for (int e = g.head[u]; e >= 0; e = g.next[e]) { + if (tree[u] == e) v = g.dst[e]; + else if (dist[g.dst[e]] < INF) { + auto delta = g.weight[e] - dist[g.src[e]] + dist[g.dst[e]]; + deviation[u] = merge(deviation[u], new Node({e, delta})); + } + } + if (v >= 0) deviation[u] = merge(deviation[u], deviation[v]); + } + } + KShortestPaths(Graph g_, int t) : g(g_) { + reverseDijkstra(t); + buildHeap(); + } + void enumerate(int s, int kth) { + int k = 0; + Node *x = deviation[s]; + Graph::Weight len = dist[s]; + ++k; + using SearchNode = tuple; + auto comp = [](SearchNode x, SearchNode y) { return get<1>(x) > get<1>(y); }; + priority_queue, decltype(comp)> que(comp); + if (x) que.push(SearchNode(x, len + x->delta)); + while (!que.empty() && k < kth) { + tie(x, len) = que.top(); que.pop(); + int e = x->e, u = g.src[e], v = g.dst[e]; + cout << len << endl; ++k; + if (deviation[v]) que.push(SearchNode(deviation[v], len+deviation[v]->delta)); + for (Node *y: {x->left, x->right}) + if (y) que.push(SearchNode(y, len + y->delta-x->delta)); + } + while (k < kth) { cout << -1 << endl; ++k; } + } +}; + +void KSH_test() { + int n = 4; + Graph g(n); + g.addEdge(0, 1, 2); + g.addEdge(0, 2, 2); + g.addEdge(1, 3, 4); + g.addEdge(2, 3, 2); + g.addEdge(1, 2, 1); + g.addEdge(2, 1, 1); + KShortestPaths ksh(g, 3); + ksh.enumerate(0, 10); +} + +void UTPC2013_10() { + int n, m, k; + scanf("%d %d %d", &n, &m, &k); + Graph g(n); + for (int i = 0; i < m; ++i) { + int u, v; + long long w; + scanf("%d %d %lld", &u, &v, &w); + g.addEdge(u, v, w); + } + KShortestPaths ksh(g, 0); + ksh.enumerate(0, k+1); +} + +int main() { + UTPC2013_10(); + //KSH_test(); +} From 4f3455f573b3f34cfe02c960dede352b12a08ba8 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Wed, 21 Mar 2018 10:03:58 +0900 Subject: [PATCH 77/80] Disjoint Sparse Table --- data_structure/disjoint_sparse_table.cc | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 data_structure/disjoint_sparse_table.cc diff --git a/data_structure/disjoint_sparse_table.cc b/data_structure/disjoint_sparse_table.cc new file mode 100644 index 0000000..87dced4 --- /dev/null +++ b/data_structure/disjoint_sparse_table.cc @@ -0,0 +1,78 @@ +// +// Disjoint Sparse Table +// +// Description: +// +// Let `otimes` be a binary associative operator. +// The disjoint sparse table is a data structure for a +// sequence xs that admits a query +// prod(i,j) = xs[i] `otimes` ... `otimes` xs[j-1] +// in time O(1). +// +// The structure is a segment tree whose node maintains +// prod(i,m) and prod(m,j) for all i, j in the segment. +// Then prod(i,j) is evaluated by finding the node that +// splits [i,j) and returning prod(i,m)*prod(m,j). +// +// Complexity: +// +// preprocessing O(n log n) +// query O(1) +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + +template +struct DisjointSparseTable { + vector> ys; + Op otimes; + DisjointSparseTable(vector xs, Op otimes_) : otimes(otimes_) { + int n = 1; + while (n <= xs.size()) n *= 2; + xs.resize(n); + ys.push_back(xs); + for (int h = 1; ; ++h) { + int range = (2 << h), half = (range /= 2); + if (range > n) break; + ys.push_back(xs); + for (int i = half; i < n; i += range) { + for (int j = i-2; j >= i-half; --j) + ys[h][j] = otimes(ys[h][j], ys[h][j+1]); + for (int j = i+1; j < min(n, i+half); ++j) + ys[h][j] = otimes(ys[h][j-1], ys[h][j]); + } + } + } + T prod(int i, int j) { // [i, j) query + --j; + int h = sizeof(int)*__CHAR_BIT__-1-__builtin_clz(i ^ j); + return otimes(ys[h][i], ys[h][j]); + } +}; +template +auto makeDisjointSparseTable(vector xs, Op op) { + return DisjointSparseTable(xs, op); +} + +int main() { + vector xs = {3,1,4,1,5,1}; + int n = xs.size(); + auto otimes = [](int a, int b) { return max(a, b); }; + auto dst = makeDisjointSparseTable(xs, otimes); + + for (int i = 0; i < n; ++i) { + for (int j = i+1; j <= n; ++j) { + cout << i << " " << j << " " << dst.prod(i, j) << " "; + int a = xs[i]; + for (int k = i+1; k < j; ++k) + a = otimes(a, xs[k]); + cout << a << endl; + } + } +} From 9cca6b826f19ed7e42dd326a4fbbb9f4d34f04d3 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Thu, 7 Jun 2018 03:36:21 +0900 Subject: [PATCH 78/80] Segment Recognizer (evaluate automaton run in O(|M|) time) --- data_structure/segment_recognizer.cc | 149 +++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 data_structure/segment_recognizer.cc diff --git a/data_structure/segment_recognizer.cc b/data_structure/segment_recognizer.cc new file mode 100644 index 0000000..6e87b4b --- /dev/null +++ b/data_structure/segment_recognizer.cc @@ -0,0 +1,149 @@ +// +// Segment Recognizer +// +// Description: +// Let M be an automaton and x be a sequence of alphabets. +// The segment recognizer computes the transitioned state +// starting from s and reading x[i,j) in O(|M|) time. +// The preprocessing requires O(|M| |x|) time and space. +// +// The same method is implemented by the segment tree, +// where the time complexity is O(log n) and the space +// complexity is O(n log n). Thus, the segment recognizer +// is efficient if |M| is small. +// +// Algorithm: +// Basically, it stores all the runs from all initial +// position i and initial state s. To reduce the space, +// it merges two runs if they yields the same state. +// +// Reference +// Mikola Bojanczyk (2009): "Factorization forests", +// International Conference on Developments in Language Theory, +// pp. 1--17. +// +#include + +using namespace std; + +#define fst first +#define snd second +#define all(c) ((c).begin()), ((c).end()) +#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); } + + +// === tick a time === +#include +double tick() { + static clock_t oldtick; + clock_t newtick = clock(); + double diff = 1.0*(newtick - oldtick) / CLOCKS_PER_SEC; + oldtick = newtick; + return diff; +} + +template +struct ModuloAutomaton { + const int init = 0; + int size() const { return MOD; } + int next(int s, int d) const { return (s+d)%MOD; } + int accept(int s) const { return s==0; } +}; + +// 0: free +// 1: selected +// 2: bottom +struct IndependenceAutomaton { + const int init = 0; + int size() const { return 3; } + int next(int s, int d) const { + if (s == 0) return d; + if (s == 1) return 2*d; + if (s == 2) return s; + } + int accept(int s) const { return s!=2; } +}; + +template +struct SegmentRecognizer { + Automaton M; + vector x; + + struct Tape { + int begin; + vector sequence; + }; + vector> index; + vector tapes; + + SegmentRecognizer(Automaton M, vector x) : M(M), x(x) { + index.assign(x.size()+1, vector(M.size())); + vector stripe; + for (int r = 0; r < M.size(); ++r) { + stripe.push_back(r); + index[0][r] = stripe[r]; + tapes.push_back({0, {r}}); + } + for (int i = 0; i < x.size(); ++i) { + unordered_set available; + for (int s = 0; s < M.size(); ++s) + available.insert(s); + vector reallocate; + for (int r = 0; r < M.size(); ++r) { + int next = M.next(tapes[stripe[r]].sequence.back(), x[i]); + if (available.count(next)) { + available.erase(next); + index[i+1][next] = stripe[r]; + tapes[stripe[r]].sequence.push_back(next); + } else { + reallocate.push_back(r); + } + } + for (int r: reallocate) { + int s = *available.begin(); + stripe[r] = tapes.size(); + index[i+1][s] = stripe[r]; + tapes.push_back({i+1, {s}}); + available.erase(s); + } + } + } + + int getState(int i, int s, int j) { + while (1) { + auto &tape = tapes[index[i][s]]; + if (j - tape.begin < tape.sequence.size()) { + return tape.sequence[j - tape.begin]; + } else { + i = tape.begin + tape.sequence.size(); + s = M.next(tape.sequence.back(), x[i-1]); + } + } + } +}; +template +SegmentRecognizer makeSegmentRecognizer(Automaton M, vector s) { + return SegmentRecognizer(M, s); +} + +int main() { + IndependenceAutomaton M; + + for (int n = 2; n < (1<<24); n*=2) { + vector x(n); + for (int i = 0; i < n; ++i) { + x[i] = (rand() % 10 == 0); + } + auto recognizer = makeSegmentRecognizer(M, x); + + tick(); + int count = 0; + for (int iter = 0; iter < n; ++iter) { + int v = (rand() % n) + 1; + int u = rand() % v; + count += recognizer.getState(u, 0, v); + } + double t = tick(); + cout << n << " " << t / n << endl; + } +} From 3a44bc12aa076c73a03184ef45e0cfb974e40161 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Thu, 9 Aug 2018 18:25:29 +0900 Subject: [PATCH 79/80] Create roc-auc.cc --- machine_learning/roc-auc.cc | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 machine_learning/roc-auc.cc diff --git a/machine_learning/roc-auc.cc b/machine_learning/roc-auc.cc new file mode 100644 index 0000000..5af7947 --- /dev/null +++ b/machine_learning/roc-auc.cc @@ -0,0 +1,40 @@ +#include + +using namespace std; + +double trapezoid(double x1, double x2, double y1, double y2) { + return (y2+y1)/2 * abs(x2-x1); +} + +double auc(vector test, vector pred) { + int n = test.size(); + assert(n == pred.size()); + + vector idx(n); + for (int i = 0; i < n; ++i) idx[i] = i; + sort(idx.begin(), idx.end(), [&](int i, int j) { return pred[i] > pred[j]; }); + + double a = 0.0; + double fp = 0, tp = 0, fp_prev = 0, tp_prev = 0; + double prev_score = -1.0/0.0; + for (int i: idx) { + if (pred[i] != prev_score) { + a += trapezoid(fp, fp_prev, tp, tp_prev); + prev_score = pred[i]; + fp_prev = fp; + tp_prev = tp; + } + if (test[i] == 1) { + tp += 1; + } else { + fp += 1; + } + } + a += trapezoid(fp, fp_prev, tp, tp_prev); + return a / (tp * fp); +} +int main() { + vector test = {0, 1, 0, 1, 1}; + vector pred = {0.2, 0.3, 0.4, 0.5, 0.6}; + cout << auc(test, pred) << endl; +} From 4fdac8202e26def25c1baf9127aaaed6a2c9f7c7 Mon Sep 17 00:00:00 2001 From: Takanori MAEHARA Date: Mon, 7 Jan 2019 09:03:05 +0900 Subject: [PATCH 80/80] debug incorrect implementation of undoable union find. (path compression cannot be undo) --- data_structure/union_find_undo.cc | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/data_structure/union_find_undo.cc b/data_structure/union_find_undo.cc index 4aff857..208ff82 100644 --- a/data_structure/union_find_undo.cc +++ b/data_structure/union_find_undo.cc @@ -27,25 +27,29 @@ struct UndoableUnionFind { UndoableUnionFind(int n) : parent(n, -1) { }; bool unite(int u, int v) { u = root(u); v = root(v); - if (u == v) return false; - if (parent[u] > parent[v]) swap(u, v); - history.push_back(make_tuple(u, v, parent[v])); - parent[u] += parent[v]; parent[v] = u; - return true; + if (u == v) { + history.push_back(make_tuple(-1,-1,-1)); + return false; + } else { + if (parent[u] > parent[v]) swap(u, v); + history.push_back(make_tuple(u, v, parent[v])); + parent[u] += parent[v]; parent[v] = u; + return true; + } } void undo() { int u, v, w; tie(u, v, w) = history.back(); history.pop_back(); + if (u == -1) return; parent[v] = w; parent[u] -= parent[v]; } bool find(int u, int v) { return root(u) == root(v); } - int root(int u) { return parent[u] < 0 ? u : parent[u] = root(parent[u]); } + int root(int u) { while (parent[u] >= 0) u = parent[u]; return u; } int size(int u) { return -parent[root(u)]; } }; - struct OfflineDynamicConnectivity { int n; UndoableUnionFind uf;