|
| 1 | +// Rozwiązanie zadania 'Ciąg binarny' z II etapu XXXI OI. |
| 2 | +// Autor rozwiązania: Paweł Putra |
| 3 | +// Złożoność czasowa: O((n + q) * log(n)) |
| 4 | +// Złożoność pamięciowa: O(n * log(n)) |
| 5 | +// Punkty: 100 (upsolve) |
| 6 | + |
| 7 | +#include <bits/stdc++.h> |
| 8 | +#define dbg(x) " " << #x << " = " << x << " " |
| 9 | +using namespace std; |
| 10 | +constexpr int MAXN = 1'000'005, MAX_S = 1'000'000'000; |
| 11 | +int n, q; |
| 12 | + |
| 13 | +int d[MAXN], pref[MAXN]; |
| 14 | + |
| 15 | +struct Blok { |
| 16 | + int l, r, len; |
| 17 | +} blok[MAXN]; |
| 18 | + |
| 19 | +// Zwraca parę (długość, koszt). |
| 20 | +pair<int, int> utnij(Blok &blok, int zl, int zr) { |
| 21 | + int l = max(zl, blok.l); |
| 22 | + int r = min(zr, blok.r); |
| 23 | + return {r - l + 1, 2 - (l == zl) - (r == zr)}; |
| 24 | +} |
| 25 | + |
| 26 | + |
| 27 | +// Zwraca pierwszy blok z końcem >= x. |
| 28 | +int znajdz_blok(int x) { |
| 29 | + int lo = 0, hi = (n + 1) / 2; |
| 30 | + while (lo < hi) { |
| 31 | + int mid = (lo + hi) / 2; |
| 32 | + if (blok[mid].r < x) lo = mid + 1; |
| 33 | + else hi = mid; |
| 34 | + } |
| 35 | + |
| 36 | + return lo; |
| 37 | +} |
| 38 | + |
| 39 | +// Zbita reprezentacja mapowań w jeden wektor bo overhead bardzo bolał. |
| 40 | +vector<int> c; |
| 41 | +vector<long long> preff = {0}; |
| 42 | +int NODES = 1; |
| 43 | +struct WaveNode { |
| 44 | + int lChild = 0, rChild = 0, offset, poffset; |
| 45 | + |
| 46 | + int mapLeft(int i) { |
| 47 | + if (i < 0) return -1; |
| 48 | + return i - c[offset+i]; |
| 49 | + } |
| 50 | + |
| 51 | + int mapRight(int i) { |
| 52 | + if (i < 0) return -1; |
| 53 | + return c[offset+i] - 1; |
| 54 | + } |
| 55 | + |
| 56 | + int sum(int l, int r) { |
| 57 | + return preff[poffset+r] - preff[poffset+l-1]; |
| 58 | + } |
| 59 | +} wave[MAXN * 30]; |
| 60 | + |
| 61 | +int sk[MAXN]; |
| 62 | +vector<int> dlugosci, sorted, ord; |
| 63 | +void wave_build(vector<int> &v, int pos = 1, int lo = 0, int hi = ord.size()) { |
| 64 | + wave[pos].poffset = preff.size(); |
| 65 | + for (auto i : v) { |
| 66 | + preff.push_back(preff.back() + dlugosci[i]); |
| 67 | + } |
| 68 | + if (lo == hi || v.empty()) { |
| 69 | + return; |
| 70 | + } |
| 71 | + |
| 72 | + int m = (lo + hi) / 2; |
| 73 | + vector<int> l, r; |
| 74 | + wave[pos].offset = c.size(); |
| 75 | + for (auto i : v) { |
| 76 | + if (sk[i] <= m) l.push_back(i); |
| 77 | + else r.push_back(i); |
| 78 | + c.push_back(r.size()); |
| 79 | + } |
| 80 | + |
| 81 | + wave[pos].lChild = ++NODES; |
| 82 | + wave_build(l, wave[pos].lChild, lo, m); |
| 83 | + wave[pos].rChild = ++NODES; |
| 84 | + wave_build(r, wave[pos].rChild, m+1, hi); |
| 85 | +} |
| 86 | + |
| 87 | +// Szuka największych elementów w przedziale. |
| 88 | +// Zwraca (liczba_elementów, suma_elementów) |
| 89 | +pair<int,int> wave_query(int pos, int l, int r, int k, int lo = 0, int hi = ord.size()) { |
| 90 | + // cerr << __func__ << dbg(pos) << dbg(l) << dbg(r) << dbg(k) << dbg(lo) << dbg(hi) << "=> " << dbg(wave[pos].sum(l, r)) << endl; |
| 91 | + if (!pos || l > r) return {0, 0}; |
| 92 | + if (r - l + 1 <= k) { |
| 93 | + return {r - l + 1, wave[pos].sum(l, r)}; |
| 94 | + } |
| 95 | + // Zachodzi r - l + 1 > k |
| 96 | + if (lo == hi) { |
| 97 | + return {k, k * wave[pos].sum(l, l)}; |
| 98 | + } |
| 99 | + |
| 100 | + int m = (lo + hi) / 2; |
| 101 | + auto [cnt, sum] = wave_query(wave[pos].rChild, wave[pos].mapRight(l-1)+1, wave[pos].mapRight(r), k, m+1, hi); |
| 102 | + k -= cnt; |
| 103 | + if (k > 0) { |
| 104 | + auto [l_cnt, l_sum] = wave_query(wave[pos].lChild, wave[pos].mapLeft(l-1)+1, wave[pos].mapLeft(r), k, lo, m); |
| 105 | + cnt += l_cnt; |
| 106 | + sum += l_sum; |
| 107 | + } |
| 108 | + |
| 109 | + return {cnt, sum}; |
| 110 | +} |
| 111 | + |
| 112 | + |
| 113 | +int32_t main() { |
| 114 | + ios_base::sync_with_stdio(0); |
| 115 | + cin.tie(0); |
| 116 | + cin >> n >> q; |
| 117 | + for (int i = 1; i <= n; i++) { |
| 118 | + cin >> d[i]; |
| 119 | + pref[i] = pref[i - 1] + d[i]; |
| 120 | + } |
| 121 | + |
| 122 | + // Interesują mnie tylko jedynki. |
| 123 | + for (int i = 0; 2*i + 1 <= n; i++) { |
| 124 | + blok[i].l = pref[2*i+1] - d[2*i+1] + 1; |
| 125 | + blok[i].r = pref[2*i+1]; |
| 126 | + blok[i].len = blok[i].r - blok[i].l + 1; |
| 127 | + dlugosci.push_back(blok[i].len); |
| 128 | + ord.push_back(ord.size()); |
| 129 | + } |
| 130 | + |
| 131 | + // Skalujemy współrzędne, żeby było q * log(n) zamiast q * log(S). |
| 132 | + sorted = ord; |
| 133 | + sort(sorted.begin(), sorted.end(), [&](const auto &lhs, const auto &rhs) { |
| 134 | + return dlugosci[lhs] < dlugosci[rhs]; |
| 135 | + }); |
| 136 | + |
| 137 | + for (int i = 0; i < (int)sorted.size(); i++) sk[sorted[i]] = i; |
| 138 | + |
| 139 | + wave_build(ord); |
| 140 | + for (int i = 1; i <= q; i++) { |
| 141 | + int l, r, k; |
| 142 | + cin >> l >> r >> k; |
| 143 | + |
| 144 | + int wynik = 0; |
| 145 | + |
| 146 | + // Znajdujemy pierwszy i ostatni blok jedynek z którym mamy przecięcie. |
| 147 | + int l_blok = znajdz_blok(l); |
| 148 | + int r_blok = znajdz_blok(r); |
| 149 | + if (blok[r_blok].l > r) r_blok--; |
| 150 | + |
| 151 | + // Zależnie od tego czy stykają się z końcem przedziału z zapytania kosztują 0/1/2 zmian. |
| 152 | + auto [dlugosc_l, koszt_l] = utnij(blok[l_blok], l, r); |
| 153 | + auto [dlugosc_r, koszt_r] = utnij(blok[r_blok], l, r); |
| 154 | + |
| 155 | + // Najpierw sprzątamy trywialne zapytania. |
| 156 | + if (l_blok > r_blok || dlugosc_l <= 0) { |
| 157 | + // Nie ma żadnej jedynki w przedziale. |
| 158 | + wynik = 0; |
| 159 | + } else if (l_blok == r_blok) { |
| 160 | + // Jest tylko jeden przedział jedynek z którym się przecinamy. |
| 161 | + wynik = (koszt_l <= k ? dlugosc_l : 0); |
| 162 | + } else if (l_blok + 1 == r_blok) { |
| 163 | + // Są dokładnie dwa przedziały z jedynkami (mogą być ucięte). |
| 164 | + wynik = max({ |
| 165 | + (dlugosc_l + dlugosc_r) * (koszt_l + koszt_r <= k), // Wzięte oba. |
| 166 | + dlugosc_l * (koszt_l <= k), // Tylko lewy. |
| 167 | + dlugosc_r * (koszt_r <= k), // Tylko prawy. |
| 168 | + }); |
| 169 | + } else { |
| 170 | + // Cztery przypadki, względem tego czy bierzemy skrajne czy nie. |
| 171 | + // Zależnie od tego trzeba różnie postępować. |
| 172 | + auto maksuj = [&](int qk, int bonus) { |
| 173 | + if (qk < 0) return; |
| 174 | + int ql = l_blok + 1; |
| 175 | + int qr = r_blok - 1; |
| 176 | + qk = min(qk / 2, qr - ql + 1); |
| 177 | + if (qk < 0) return; |
| 178 | + if (qk == 0) { |
| 179 | + wynik = max(wynik, bonus); |
| 180 | + return; |
| 181 | + } |
| 182 | + |
| 183 | + auto [_, sum] = wave_query(1, ql, qr, qk); |
| 184 | + // cerr << "wave_query " << dbg(ql) << dbg(qr) << dbg(qk) << "=> " << dbg(sum) << dbg(bonus) << endl; |
| 185 | + wynik = max(wynik, sum + bonus); |
| 186 | + }; |
| 187 | + |
| 188 | + // 1: Nie biorąc żadnego z dwóch. |
| 189 | + maksuj(k, 0); |
| 190 | + // 2: Biorąc tylko lewy. |
| 191 | + maksuj(k - koszt_l, dlugosc_l); |
| 192 | + // 3: Biorąc tylko prawy. |
| 193 | + maksuj(k - koszt_r, dlugosc_r); |
| 194 | + // 4: Biorąc oba. |
| 195 | + maksuj(k - koszt_l - koszt_r, dlugosc_l + dlugosc_r); |
| 196 | + } |
| 197 | + |
| 198 | + cout << wynik << "\n"; |
| 199 | + } |
| 200 | +} |
| 201 | + |
0 commit comments