Skip to Content

Fluid Dynamics

Complejidad temporal: O(SSlogS)\mathcal{O}(S\sqrt{S\log S}), donde S=N+QS=N+Q.

Mantenemos una lista enlazada de envolventes convexas de tamaño BLOCKSlogSBLOCK \approx \sqrt{S\log S} cada una. Cada actualización y consulta toma tiempo O(SlogS)\mathcal{O}(\sqrt{S\log S}). El TL es bastante ajustado …

#include <bits/stdc++.h> using namespace std; using ll = long long; using ld = long double; using db = double; using str = string; // yay python! using pi = pair<int, int>; using pl = pair<ll, ll>; using pd = pair<db, db>; using vi = vector<int>; using vb = vector<bool>; using vl = vector<ll>; using vd = vector<db>; using vs = vector<str>; using vpi = vector<pi>; using vpl = vector<pl>; using vpd = vector<pd>; #define tcT template <class T #define tcTU tcT, class U // ^ lol this makes everything look weird but I'll try it tcT > using V = vector<T>; tcT, size_t SZ > using AR = array<T, SZ>; tcT > using PR = pair<T, T>; // pairs #define mp make_pair #define f first #define s second // vectors // oops size(x), rbegin(x), rend(x) need C++17 #define sz(x) int((x).size()) #define bg(x) begin(x) #define all(x) bg(x), end(x) #define rall(x) x.rbegin(), x.rend() #define sor(x) sort(all(x)) #define rsz resize #define ins insert #define ft front() #define bk back() #define pb push_back #define eb emplace_back #define pf push_front #define lb lower_bound #define ub upper_bound tcT > int lwb(V<T> &a, const T &b) { return int(lb(all(a), b) - bg(a)); } // loops #define FOR(i, a, b) for (int i = (a); i < (b); ++i) #define F0R(i, a) FOR(i, 0, a) #define ROF(i, a, b) for (int i = (b) - 1; i >= (a); --i) #define R0F(i, a) ROF(i, 0, a) #define trav(a, x) for (auto &a : x) const int MOD = 1e9 + 7; // 998244353; const int MX = 2e5 + 5; const ll INF = 1e18; // not too close to LLONG_MAX const ld PI = acos((ld)-1); const int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1}; // for every grid problem!! mt19937 rng((uint32_t)chrono::steady_clock::now().time_since_epoch().count()); template <class T> using pqg = priority_queue<T, vector<T>, greater<T>>; // bitwise ops // also see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html constexpr int pct(int x) { return __builtin_popcount(x); } // # of bits set constexpr int bits(int x) { // assert(x >= 0); // make C++11 compatible until // USACO updates ... return x == 0 ? 0 : 31 - __builtin_clz(x); } // floor(log2(x)) constexpr int p2(int x) { return 1 << x; } constexpr int msk2(int x) { return p2(x) - 1; } ll cdiv(ll a, ll b) { return a / b + ((a ^ b) > 0 && a % b); } // divide a by b rounded up ll fdiv(ll a, ll b) { return a / b - ((a ^ b) < 0 && a % b); } // divide a by b rounded down tcT > bool ckmin(T &a, const T &b) { return b < a ? a = b, 1 : 0; } // set a = min(a,b) tcT > bool ckmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; } tcTU > T fstTrue(T lo, T hi, U f) { hi++; assert(lo <= hi); // assuming f is increasing while (lo < hi) { // find first index such that f is true T mid = lo + (hi - lo) / 2; f(mid) ? hi = mid : lo = mid + 1; } return lo; } tcTU > T lstTrue(T lo, T hi, U f) { lo--; assert(lo <= hi); // assuming f is decreasing while (lo < hi) { // find first index such that f is true T mid = lo + (hi - lo + 1) / 2; f(mid) ? lo = mid : hi = mid - 1; } return lo; } tcT > void remDup(vector<T> &v) { // sort and remove duplicates sort(all(v)); v.erase(unique(all(v)), end(v)); } tcTU > void erase(T &t, const U &u) { // don't erase auto it = t.find(u); assert(it != end(t)); t.erase(it); } // element that doesn't exist from (multi)set // INPUT #define tcTUU tcT, class... U tcT > void re(complex<T> &c); tcTU > void re(pair<T, U> &p); tcT > void re(V<T> &v); tcT, size_t SZ > void re(AR<T, SZ> &a); tcT > void re(T &x) { cin >> x; } void re(db &d) { str t; re(t); d = stod(t); } void re(ld &d) { str t; re(t); d = stold(t); } tcTUU > void re(T &t, U &...u) { re(t); re(u...); } tcT > void re(complex<T> &c) { T a, b; re(a, b); c = {a, b}; } tcTU > void re(pair<T, U> &p) { re(p.f, p.s); } tcT > void re(V<T> &x) { trav(a, x) re(a); } tcT, size_t SZ > void re(AR<T, SZ> &x) { trav(a, x) re(a); } tcT > void rv(int n, V<T> &x) { x.rsz(n); re(x); } // TO_STRING #define ts to_string str ts(char c) { return str(1, c); } str ts(const char *s) { return (str)s; } str ts(str s) { return s; } str ts(bool b) { #ifdef LOCAL return b ? "true" : "false"; #else return ts((int)b); #endif } tcT > str ts(complex<T> c) { stringstream ss; ss << c; return ss.str(); } str ts(V<bool> v) { str res = "{"; F0R(i, sz(v)) res += char('0' + v[i]); res += "}"; return res; } template <size_t SZ> str ts(bitset<SZ> b) { str res = ""; F0R(i, SZ) res += char('0' + b[i]); return res; } tcTU > str ts(pair<T, U> p); tcT > str ts(T v) { // containers with begin(), end() #ifdef LOCAL bool fst = 1; str res = "{"; for (const auto &x : v) { if (!fst) res += ", "; fst = 0; res += ts(x); } res += "}"; return res; #else bool fst = 1; str res = ""; for (const auto &x : v) { if (!fst) res += " "; fst = 0; res += ts(x); } return res; #endif } tcTU > str ts(pair<T, U> p) { #ifdef LOCAL return "(" + ts(p.f) + ", " + ts(p.s) + ")"; #else return ts(p.f) + " " + ts(p.s); #endif } // OUTPUT tcT > void pr(T x) { cout << ts(x); } tcTUU > void pr(const T &t, const U &...u) { pr(t); pr(u...); } void ps() { pr("\n"); } // print w/ spaces tcTUU > void ps(const T &t, const U &...u) { pr(t); if (sizeof...(u)) pr(" "); ps(u...); } // DEBUG void DBG() { cerr << "]" << endl; } tcTUU > void DBG(const T &t, const U &...u) { cerr << ts(t); if (sizeof...(u)) cerr << ", "; DBG(u...); } #ifdef LOCAL // compile with -DLOCAL, chk -> fake assert #define dbg(...) \ cerr << "Line(" << __LINE__ << ") -> [" << #__VA_ARGS__ << "]: [", DBG(__VA_ARGS__) #define chk(...) \ if (!(__VA_ARGS__)) \ cerr << "Line(" << __LINE__ << ") -> function(" << __FUNCTION__ \ << ") -> CHK FAILED: (" << #__VA_ARGS__ << ")" << "\n", \ exit(0); #else #define dbg(...) 0 #define chk(...) 0 #endif void setPrec() { cout << fixed << setprecision(15); } void unsyncIO() { cin.tie(0)->sync_with_stdio(0); } // FILE I/O void setIn(str s) { freopen(s.c_str(), "r", stdin); } void setOut(str s) { freopen(s.c_str(), "w", stdout); } void setIO(str s = "") { unsyncIO(); setPrec(); // cin.exceptions(cin.failbit); // throws exception when do smth illegal // ex. try to read letter into int if (sz(s)) setIn(s + ".in"), setOut(s + ".out"); // for USACO } #pragma GCC optimize("Ofast") #pragma GCC target("avx2") // template code const int BLOCK = 2000; int N, Q; ll W; typedef pl P; typedef vector<P> vP; vP fluid; ll CROSS(const P &a, const P &b) { return a.f * b.s - a.s * b.f; } ll CROSS(const P &p, const P &a, const P &b) { return (a.f - p.f) * (b.s - p.s) - (a.s - p.s) * (b.f - p.f); } ll cross(int a, int b, int c) { return CROSS(fluid[a], fluid[b], fluid[c]); } ll dif(int a, int x) { return fluid[x].f * fluid[a].s - fluid[x].s * fluid[a].f; } ll bs(int *v, const P &p, int lo, int hi) { auto eval = [&](int x) { return fluid[x].f * p.f + fluid[x].s * p.s; }; while (lo < hi) { int mid = (lo + hi) / 2; if (eval(v[mid]) < eval(v[mid + 1])) hi = mid; else lo = mid + 1; } return eval(v[lo]); } bool cmp(int a, int b) { return fluid[a] < fluid[b]; } struct Hull { int dat[BLOCK], lower[BLOCK]; int dsz = 0, usz = 0, lsz = 0; ll minDif(int x) { // get min of pl p = {-fluid[x].s, fluid[x].f}; return bs(lower, p, 0, lsz - 1); } bool bad(int x) { return minDif(x) >= W; } void build() { vi xord; F0R(i, dsz) xord.pb(dat[i]); sort(all(xord), cmp); usz = lsz = 0; for (int i : xord) { while (lsz > 1 && cross(lower[lsz - 2], lower[lsz - 1], i) <= 0) lsz--; lower[lsz++] = i; } } void prin(int *a, int &b) { F0R(i, b) dbg(a[i]); dbg("----"); } void INS(int *a, int &b, int pos, int label) { ROF(i, pos + 1, b + 1) a[i] = a[i - 1]; a[pos] = label; b++; } void ERASE(int *a, int &b, int l, int r) { int dif = r - l; if (dif <= 0) return; FOR(i, r, b) a[i - dif] = a[i]; b -= dif; } void insLower(int label) { int pos = lb(lower, lower + lsz, label, cmp) - lower; if (pos && pos < lsz) { if (cross(lower[pos - 1], lower[pos], label) >= 0) return; } INS(lower, lsz, pos, label); { int nex = pos + 1; while (nex + 1 < lsz && cross(lower[pos], lower[nex], lower[nex + 1]) <= 0) nex++; ERASE(lower, lsz, pos + 1, nex); } { int nex = pos - 1; while (nex > 0 && cross(lower[nex - 1], lower[nex], lower[pos]) <= 0) nex--; ERASE(lower, lsz, nex + 1, pos); } } void ins(int label) { int i = dsz; while (i && dif(dat[i - 1], label) >= W) i--; INS(dat, dsz, i, label); insLower(label); } }; vector<Hull> hull; vi NEX, PRE; int lst = -1; int num; int ad() { hull.pb(Hull()); NEX.pb(-1), PRE.pb(-1); return sz(hull) - 1; } void link(int a, int b) { NEX[a] = b; if (b == -1) lst = a; else PRE[b] = a; } int ti = 0; void split(int cur) { int SZ = hull[cur].dsz; assert(SZ <= BLOCK); if (SZ != BLOCK) return; int CUR = ad(); int aft = NEX[cur]; link(cur, CUR); link(CUR, aft); int *v = hull[cur].dat; int m = SZ / 2; ++ti; assert(hull[CUR].dsz == 0); FOR(i, m, SZ) { int t = v[i]; hull[CUR].dat[hull[CUR].dsz++] = t; } hull[cur].dsz = m; assert(hull[CUR].dsz == m); hull[cur].build(); hull[CUR].build(); // split block into two } void ins(int v, int c) { num++; int label = sz(fluid); fluid.pb({v, c}); int cur = lst, pre = -1; while (cur != -1 && hull[cur].bad(label)) { pre = cur; cur = PRE[cur]; } if (cur == -1) { if (pre == -1) { assert(sz(fluid) == 1); lst = pre = ad(); } cur = pre; } hull[cur].ins(label); split(cur); } int get(int k) { k = num + 1 - k; assert(k); for (int cur = lst;; cur = PRE[cur]) { assert(cur != -1); int *v = hull[cur].dat, SZ = hull[cur].dsz; if (SZ >= k) return v[SZ - k]; k -= SZ; } exit(5); } int main() { clock_t beg = clock(); setIO(); re(N, Q, W); F0R(i, N) { int v, c; re(v, c); ins(v, c); } dbg("DONE WITH INSERTS", (db)(clock() - beg) / CLOCKS_PER_SEC); F0R(i, Q) { str s; re(s); if (s == "INSERT") { int v, c; re(v, c); ins(v, c); } else { int k; re(k); int p = get(k); ps(fluid[p].f, fluid[p].s); } } dbg((db)(clock() - beg) / CLOCKS_PER_SEC); }