Skip to content

Commit ddc1b90

Browse files
Dodano rozwiązania zadania 'Ciąg binarny' z XXXI OI
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
1 parent 27daeb8 commit ddc1b90

File tree

6 files changed

+465
-8
lines changed

6 files changed

+465
-8
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ git pull
8787

8888
Etap I | Etap II | Etap III | Łącznie
8989
:---: | :---: | :---: | :---:
90-
43/150 (29%) | 53/157 (34%) | 42/200 (21%) | 138/507 (27%)
90+
43/150 (29%) | 54/157 (34%) | 42/200 (21%) | 139/507 (27%)
9191

9292
## Rozwiązane zadania wg edycji
9393

9494
Edycja | Wynik | Edycja | Wynik | Edycja | Wynik | Edycja | Wynik
9595
:--- | ---: | :--- | ---: | :--- | ---: | :--- | ---:
96-
I | 3/10 (30%) | XI | 2/16 (12%) | XXI | 6/17 (35%) | XXXI | 4/17 (24%)
96+
I | 3/10 (30%) | XI | 2/16 (12%) | XXI | 6/17 (35%) | XXXI | 5/17 (29%)
9797
II | 4/14 (29%) | XII | 4/17 (24%) | XXII | 6/17 (35%) | XXXII | 7/18 (39%)
9898
III | 2/13 (15%) | XIII | 5/16 (31%) | XXIII | 3/17 (18%) | |
9999
IV | 3/16 (19%) | XIV | 3/16 (19%) | XXIV | 7/16 (44%) | |
@@ -180,7 +180,7 @@ XXVII | | czw ✅ | | |
180180
XXVIII | | ple ✅ | | pla ✅ |
181181
XXIX | | | | kon ✅ | bom ✅
182182
XXX | | | | |
183-
XXXI | | lic ✅ | ryc ✅ | poj ✅ |
183+
XXXI | | lic ✅ | ryc ✅ | cia ✅ | poj ✅
184184
XXXII | sss ✅ | drz ✅ | | |
185185
</details>
186186

checklista/oi_progress.svg

Lines changed: 5 additions & 5 deletions
Loading

checklista/tasks.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,13 @@
888888
"nazwa": "kol",
889889
"punkty": 100
890890
},
891+
"xxxi_etap2_dzien2_cia": {
892+
"edycja": 31,
893+
"etap": 2,
894+
"dzien": 2,
895+
"nazwa": "cia",
896+
"punkty": 100
897+
},
891898
"xxxi_etap2_dzien2_poj": {
892899
"edycja": 31,
893900
"etap": 2,
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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

Comments
 (0)