Chcemy przygotować mieszankę przypraw, w której sól, pieprz i czosnek są w proporcji
Zastanówmy się najpierw, jak wyglądałoby rozwiązanie zadania, gdybyśmy mieli tylko
dwie przyprawy (czyli
Każda butelka zawiera mieszankę soli i pieprzu w pewnym stosunku.
Zatem dla
Zastanówmy się, jakie proporcje możemy uzyskać, mieszając zawartości
dwóch butelek
Geometrycznie można to przedstawić tak, że uzyskamy wszystkie liczby na osi
liczbowej pomiędzy
Widać zatem, że nie będziemy potrzebowali więcej niż dwóch butelek.
Bo jeśli mamy trzy butelki
Ostatecznie kryterium jest następujące:
jeśli
Tak więc cały algorytm jest następujący: trzymamy wartości
Zatem całe rozwiązanie będzie działać w złożoności czasowej
W przypadku trzech przypraw, możemy stosunek
Dla dwóch butelek
Tak więc, jeśli punkt
Jeśli mamy trzy punkty
Okazuje się, że podobnie jak dla dwóch przypraw wystarczały dwie butelki, to w przypadku
trzech przypraw zawsze wystarczą trzy butelki (o ile rozwiązanie istnieje).
Wynika to z tego, że dla większej liczby punktów
Rozwiązanie zaimplementujemy z użyciem liczb całkowitych, zatem punkty
Aby stwierdzić, czy odpowiedź jest równa
Aby stwierdzić, czy odpowiedź jest równa
W końcu, aby stwierdzić, czy odpowiedź jest równa
Można pokazać, że jeśli
Będziemy zatem utrzymywać liczbę kątów
Złożoność czasowa algorytmu będzie wynosić
const int N = 110000; int sf,pf,gf,n,k; int cnt_one,cnt_two,cnt_three; struct point { ll x,y,mian; point() : x(), y(), mian(1) { } point(ll _x, ll _y, ll _mian) { x = _x; y = _y; mian = _mian; ll d = __gcd(__gcd(x, y), mian); if (d < 0) { d *= -1; } x /= d; y /= d; mian /= d; if (mian < 0) { x *= -1; y *= -1; mian *= -1; } } }; bool operator==(const point& p, const point& q) { return p.x == q.x && p.y == q.y && p.mian == q.mian; } struct angle { ll x,y; angle(ll _x, ll _y) { x = _x; y = _y; ll d = __gcd(x, y); if (d < 0) { d *= -1; } x /= d; y /= d; } bool up() const { return y > 0 || y == 0 && x > 0; } }; __int128_t det(const angle& p, const angle& q) { return __int128_t(p.x) * q.y - __int128_t(p.y) * q.x; } bool operator<(const angle& p, const angle& q) { if (p.up() != q.up()) { return p.up(); } else { return det(p, q) > 0; } } bool operator==(const angle& p, const angle& q) { return !(p < q) && !(q < p); } bool bigger(const angle& p, const angle& q) { return det(p, q) < 0; } angle operator-(const angle& p) { return angle(-p.x, -p.y); } angle operator-(const point& p, const point& q) { ll x = p.x * q.mian - q.x * p.mian; ll y = p.y * q.mian - q.y * p.mian; return angle(x, y); } point pt[N],zero; multiset<angle> zbior_katy; int calc(multiset<angle>::iterator& it) { auto prev_it = prev(it == zbior_katy.begin() ? zbior_katy.end() : it); auto next_it = next(it) == zbior_katy.end() ? zbior_katy.begin() : next(it); return bigger(*prev_it, *it) + bigger(*it, *next_it) - bigger(*prev_it, *next_it); } bool three() { if (zbior_katy.empty()) return 0; if (zbior_katy.count(*zbior_katy.begin()) == zbior_katy.size()) return 0; return cnt_three == 0; } int main() { scanf("%d%d%d%d",&sf,&pf,&gf,&n); zero = point(sf, pf, sf+pf+gf); REP(i,n) { char typ; scanf(" %c",&typ); if (typ == 'A') { int s,p,f; scanf("%d%d%d",&s,&p,&f); pt[k] = point(s, p, s+p+f); if (pt[k] == zero) { cnt_one++; } else { angle kat = pt[k] - zero; auto it = zbior_katy.insert(kat); cnt_three += calc(it); cnt_two += zbior_katy.count(-kat); } k++; } else { int x; scanf("%d",&x); --x; if (pt[x] == zero) { cnt_one--; } else { angle kat = pt[x] - zero; auto it = zbior_katy.find(kat); cnt_three -= calc(it); zbior_katy.erase(it); cnt_two -= zbior_katy.count(-kat); } } int ans = 0; if (cnt_one > 0) { ans = 1; } else if (cnt_two > 0) { ans = 2; } else if (three()) { ans = 3; } printf("%d\n", ans); } }