A. Komiljon va shokolad
Komiljon uchchala jiyanlariga bir xil sondagi shokolad olib borishi kerak. Bundan Komiljon olib boradigan shokoladlar soni 3 ga bo`linishi zarurligi kelib chiqadi. Demak u ko`pi bilan
\(\lfloor \frac{n}{3} \rfloor \cdot 3\) ta shokolad olib boroladi.
Time complexity: \(O(1)\)
print(int(input()) // 3 * 3)
B. Komiljon va uning do'stlari
Komiljon va uning do`stining uylari \(N\) ta uyning aynan qaysisi ekanligi no'malum. Ammo minimal va maksimal masofani topish kerak. \(N\) ning soni kichik bo`lganligi uchun, barcha holatlarni ko`rib chiqish mumkindir. Komiljonning uyini \(i\), do`stining uyini \(j\) deb belgilasak
manhettan masofani \(|x_i - x_j| + |y_i - y_j|\) ko`rinishida ifodalash mumkin. Bularning barchasidan minimal va maksimalini topish yetarli.
Time complexity: \(O(N^2)\)
n = int(input())
coor = [list(map(int, input().split())) for _ in range(n)]
min_ans, max_ans = 10 ** 10, -10 ** 10
for i in range(n):
xi, yi = coor[i]
for j in range(n):
if i == j: continue
xj, yj = coor[j]
min_ans = min(min_ans, abs(xi - xj) + abs(yi - yj))
max_ans = max(max_ans, abs(xi - xj) + abs(yi - yj))
print(min_ans, max_ans)
C. Banknotalar
Observation 1: Banktonalar bir-biriga bo`linadi, demak istalgan miqdordagi summani minimal sondagi banknotalar bilan yig`ish uchun har doim iloji boricha eng katta banknotalardan foydalanish lozim.
Observation 2: Banktonalar bir-biriga bo`linadi, qiymati turli xil banknotalar soni ko`p emas, va aniqrog`i xilma-xil banknotalar soni 60 tadan ko`p bo`la olmaydi. Isboti: banknotalarni saralasak, istalgan banknota keyingi banknotaga bo`linadi. Demak ular bir biriga teng, yoki biri ikkinchisidan kamida 2 marta katta. Bundan turli xil banknotalar soni ko`pi bilan\(\log_2(10^{18}) \approx 60\) ekanligi kelib chiqadi.
Xilma-xil banknotalarni va ularning nechtadan ekanligini saqlaymiz. Har bir so`rovd uchun banknotalarni kattasidan kichigiga ko`rib chiqamiz va
har bir banknotdan iloji boricha ko`p ishlatamiz. Summani yasab bo`lmaydigan holatlarga ehtiyot bo`ling.
Time complexity: \(O(N\log{N} + QC)\). Bu yerda \(C\) bu turli banknotalar soni yoki \(C \approx \log_2(\max{A}) \approx 60\).
n = int(input())
a = sorted(list(map(int, input().split())), reverse = True)
banknotes = []
count = []
i = 0
while i < n:
c = 1
while i + c < n and a[i + c] == a[i]:
c += 1
banknotes.append(a[i])
count.append(c)
i += c
m = len(banknotes)
q = int(input())
queries = list(map(int, input().split()))
for y in queries:
cur = 0
for i in range(m):
k = min(count[i], y // banknotes[i])
cur += k
y -= k * banknotes[i]
print([-1, cur][y == 0], end = ' ')
D. Shifuning topshirig`i
Observation 1: \((i, j)\) juftlik uchun \(|A_i - A_j| = 1\) bajarilishi kerak, demak \(A_i\) va \(A_j\) sonlarini 2 ga bo`lingandagi qoldiqlari har xil.
Demak tugunlarni massiv elementlari va qirralarni \((X_i, Y_i)\) cheklovlardan iborat grafni olsak, ushbu graf bipartite bo`lishi lozim. Aks holda natija \(-1\). Agar bipartitelik sharti qanoatlantirsa, grafdagi birinchi bo`lak elementlarini 1 qilib va ikkinchi bo`lak elementlarini 2 qilib o`rnatsak, masalada so`ralgan massivni
qura olamiz.
Time complexity: \(O(N + M)\)
import sys
# python tilida rekursiyaga chegara qo`yilgan
# mana shu qator yordamida bu chegarani oshirsa bo`ladi
sys.setrecursionlimit(10 ** 9)
n, m = map(int, input().split())
g = [[] for i in range(n + 1)]
for i in range(m):
x, y = map(int, input().split())
g[x].append(y)
g[y].append(x)
ans = [0 for _ in range(n + 1)]
def dfs(i, c):
ans[i] = c
for j in g[i]:
if ans[j] == 0:
if not dfs(j, 1 + (c % 2)):
return False
elif ans[i] == ans[j]:
return False
return True
for i in range(1, n+1):
if ans[i] == 0:
if not dfs(i, 1):
exit(print(-1))
for i in range(1, n+1):
if ans[i] == -1:
ans[i] = 0
print(ans[i], end = ' ')
E. Asilbek va g'aroyib oraliqlar
Observation: Ixtiyoriy \(l, r\) uchun \(F(l, r) \le F(l,r+1)\) shart bajariladi.
Isbot: Agar \(F(l, r) > F(l, r+1)\) bo'lsa, u holda \(r+1\)-elementni yo'q deb tasavvur qilib \(F(l,r+1)\) qiymatga (ya'ni kichikroq qiymatga) erishishimiz mumkin. Ziddiyat.
Bu turdagi masalalarda ikkita ko'rsatkich (two-pointers) usuli yordam berishi mumkin. Aytaylik \(l\) va \(r\) ko'rsatkichlarimiz bor. Agar shu oraliqdagi sonlarni STL Multiset yoki Segment Treeda saqlasak, \(r \rightarrow r+1\) siljishni \(O(\log{N})\)da bajarish mumkin. Jami siljishlar soni esa \(O(N)\), chunki har bir \(l\) va \(r\) faqat \(N\) marta o'zgaradi.
Complexity: \(O(N \log{N})\).
STL yordamida (by Dilyorbek):
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct split_set {
multiset<int> a{}, b{};
ll sa{}, sb{};
void check() {
if (a.size() > b.size()+1) {
int x = *a.rbegin();
b.insert(x), sb += x;
a.erase(a.find(x)), sa -= x;
} else if (a.size() < b.size()) {
int x = *b.begin();
a.insert(x), sa += x;
b.erase(b.begin()), sb -= x;
}
}
void insert(int x) {
if (a.empty() || x < *a.rbegin()) {
a.insert(x), sa += x;
} else {
b.insert(x), sb += x;
}
check();
}
void erase(int x) {
if (auto it = a.find(x); it != a.end()) {
a.erase(it), sa -= x;
} else {
b.erase(b.find(x)), sb -= x;
}
check();
}
ll calc() {
if (a.empty())
return 0;
ll med = *a.rbegin();
return med * (ll) a.size() - sa
+ sb - med * (ll) b.size();
}
};
int main() {
cin.tie(0)->sync_with_stdio(0);
ll n, t;
cin >> n >> t;
split_set st{};
vector<int> arr(n);
for (int& i : arr) cin >> i;
ll res = 0;
for (int l = 0, r = 0; l < n; l++) {
while (r < n) {
st.insert(arr[r]);
if (st.calc() > t) {
st.erase(arr[r]);
break;
} else {
r++;
}
}
res += r - l;
st.erase(arr[l]);
}
cout << res << endl;
return 0;
}
Segment Tree yordamida (by Dilshodbek):
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 20;
#define all(x) (x).begin(), (x).end()
using pii = pair<int, long long>;
int a[N], b[N], orig_val[N];
pii t[4 * N];
pii operator+ (pii x, pii y){
return make_pair(x.first + y.first, x.second + y.second);
}
void update(int v, int tl, int tr, int pos, pair<int, long long> val){
if (tl == tr){
t[v] = t[v] + val;
return;
}
int tm = (tl + tr) >> 1;
if (pos <= tm){
update(v << 1, tl, tm, pos, val);
} else {
update(v << 1 | 1, tm + 1, tr, pos, val);
}
t[v] = t[v << 1] + t[v << 1 | 1];
}
pii get(int v, int tl, int tr, int l, int r){
if (l <= tl && tr <= r) return t[v];
if (tl > r || tr < l) return make_pair(0, 0);
int tm = (tl + tr) >> 1;
return get(v << 1, tl, tm, l, r) + get(v << 1 | 1, tm + 1, tr, l, r);
}
int find(int v, int tl, int tr, int k){
if (tl == tr) return tl;
int tm = (tl + tr) >> 1;
if (t[v << 1].first >= k){
return find(v << 1, tl, tm, k);
} else {
return find(v << 1 | 1, tm + 1, tr, k - t[v << 1].first);
}
}
int n;
void add(int i){
update(1, 0, n - 1, b[i], make_pair(1, a[i]));
}
void del(int i){
update(1, 0, n - 1, b[i], make_pair(-1, -a[i]));
}
long long cost(){
int k = t[1].first;
if (k <= 1) return 0;
int mid = find(1, 0, n - 1, (k + 1) / 2);
long long ans = 0;
{
auto [cnt, sum] = get(1, 0, n - 1, 0, mid - 1);
ans += 1LL * orig_val[mid] * cnt - sum;
}
{
auto [cnt, sum] = get(1, 0, n - 1, mid + 1, n - 1);
ans += sum - 1LL * orig_val[mid] * cnt;
}
return ans;
}
int main(){
ios_base::sync_with_stdio(false);
fill(t, t + 4 * N, make_pair(0, 0));
long long T;
cin >> n >> T;
vector<int> values(n);
for (int i = 0; i < n; i++){
cin >> a[i];
values[i] = a[i];
}
sort(all(values));
values.erase(unique(all(values)), values.end());
for (int i = 0; i < values.size(); i++) orig_val[i] = values[i];
for (int i = 0; i < n; i++){
b[i] = lower_bound(all(values), a[i]) - values.begin();
}
long long ans = 0;
int j = 0;
for (int i = 0; i < n; i++){
while (j < n){
add(j);
if (cost() > T){
del(j);
break;
}
j++;
}
ans += j - i;
del(i);
}
cout << ans << endl;
return 0;
}
.