Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions doc/topic2/FenwickTree.md
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

удали из домашки плиз теорию

Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Дерево Фенвика (Fenwick Tree, Binary Indexed Tree)

**Дерево Фенвика** — это структура данных, которая позволяет эффективно вычислять **префиксные суммы** (суммы элементов массива от начала до заданной позиции) и поддерживать **обновление элементов** за время **O(log n)**. Оно требует **O(n)** памяти.

Пусть дан массив `a` длины `n`. Деревом Фенвика называется массив `t` той же длины, определённый следующим образом:

\[ t_i = \sum_{k=F(i)}^{i} a_k \]

где `F` — некоторая функция, удовлетворяющая условию \( F(i) \leq i \). Конкретное определение функции `F` будет дано позже.

### Основные операции:
1. **Запрос суммы (prefix sum)** — получение суммы элементов от начала массива до позиции `i`.
2. **Обновление элемента** — изменение значения элемента и корректировка структуры.

## Запрос суммы

Для вычисления суммы на отрезке `[l, r]` запрос сводится к двум суммам на префиксе:
\[ \text{sum}(l, r) = \text{sum}(r) - \text{sum}(l - 1) \]

Каждая из этих сумм вычисляется рекурсивно:
\[ \text{sum}(k) = t_k + \text{sum}(F(k) - 1) \]

## Запрос обновления

При изменении элемента `a[k]` необходимо обновить все ячейки `t_i`, в которых учтён этот элемент.

Функцию `F` можно выбрать таким образом, что количество операций при подсчёте суммы и обновлении будет \( O(\log n) \). Распространены два варианта функции `F`:

1. \( F_1(x) = x \& (x + 1) \)
2. \( F_2(x) = x - (x \& -x) + 1 \)

Первый вариант часто встречается в учебных материалах, таких как Викиконспекты и Емакс, и поэтому более известен. Второй вариант проще для запоминания и реализации, а также обладает большей гибкостью — например, позволяет выполнять бинарный поиск по префиксным суммам. Именно второй вариант будет использоваться в дальнейшем.

## Преимущества:
- Простота реализации.
- Эффективность: обе операции работают за **O(log n)**.
- Меньшая константа по сравнению с деревом отрезков.

## Недостатки:
- Менее гибкое, чем дерево отрезков (например, не поддерживает все виды запросов, которые можно делать в дереве отрезков).

---

## Принцип работы

Дерево Фенвика использует идею **бинарного представления индексов**. Каждый элемент дерева хранит сумму некоторого диапазона исходного массива.

## Пример для массива из 8 элементов:
```
Дерево Фенвика:
F[1] = A[1]
F[2] = A[1] + A[2]
F[3] = A[3]
F[4] = A[1] + A[2] + A[3] + A[4]
F[5] = A[5]
F[6] = A[5] + A[6]
F[7] = A[7]
F[8] = A[1] + A[2] + ... + A[8]
```

### Пример кода на C++

```cpp
#include <iostream>
#include <vector>

class FenwickTree {
private:
std::vector<int> tree;

public:
FenwickTree(int size) : tree(size + 1, 0) {}

// Обновление элемента: добавляем delta к A[pos]
void update(int pos, int delta) {
for (; pos < tree.size(); pos += pos & -pos) {
tree[pos] += delta;
}
}

// Запрос суммы: сумма элементов от A[1] до A[pos]
int query(int pos) {
int sum = 0;
for (; pos > 0; pos -= pos & -pos) {
sum += tree[pos];
}
return sum;
}

// Запрос суммы на отрезке [l, r]
int rangeQuery(int l, int r) {
return query(r) - query(l - 1);
}
};

int main() {
std::vector<int> arr = {0, 1, 2, 3, 4, 5, 6, 7, 8}; // Индексация с 1 для удобства

FenwickTree fenwick(arr.size() - 1); // Игнорируем 0-й элемент

// Построение дерева
for (int i = 1; i < arr.size(); ++i) {
fenwick.update(i, arr[i]);
}

// Запрос суммы первых 5 элементов
std::cout << "Sum of first 5 elements: " << fenwick.query(5) << std::endl; // 1+2+3+4+5 = 15

// Запрос суммы на отрезке [2, 5]
std::cout << "Sum from 2 to 5: " << fenwick.rangeQuery(2, 5) << std::endl; // 2+3+4+5 = 14

// Обновление элемента A[3] += 10
fenwick.update(3, 10);

// Проверка суммы после обновления
std::cout << "Sum of first 5 after update: " << fenwick.query(5) << std::endl; // 15 + 10 = 25

return 0;
}
```

#### Вывод программы:
```
Sum of first 5 elements: 15
Sum from 2 to 5: 14
Sum of first 5 after update: 25
```

### Применение
Дерево Фенвика часто используется в задачах:
- Подсчет суммы на префиксе или отрезке.
- Решение задач на инверсии в перестановках.
- Оптимизация динамического программирования.

Это мощный инструмент для задач, где важна скорость работы с префиксными суммами
20 changes: 19 additions & 1 deletion task_01/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
#include "solution.hpp"
#include <iostream>

int main() { return 0; }
int main() {
Comment thread
LostPointer marked this conversation as resolved.
int n;
int c;
cin>>n;
vector<int> a(n);
cin>>c;
for (int i=0; i<n; i++)
{
cin>>a[i];
}
Result result = Solve(n, c, a);

if (result.finded)
cout << a[result.left] << ' ' << a[result.right] << endl;
Comment thread
LostPointer marked this conversation as resolved.
else
cout << "Не удалось найти ответ!" << endl;

return 0; }
27 changes: 27 additions & 0 deletions task_01/src/solution.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <vector>
using namespace std;

struct Result
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

норм решение, но лучше обернуть все в std::optional, станет красивее

{
bool finded = false;
int left = 0, right = 0;
};

inline Result Solve(int n, int c, const vector<int>& a)
Copy link
Copy Markdown
Contributor

@LostPointer LostPointer Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

давай не a а array и другие имена тоже

n тут не нужна) просто сделай array.size()

Copy link
Copy Markdown
Contributor

@LostPointer LostPointer Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::optional<Result> SumOfTwo(int sum_of_two, const vector<int>& array) будет выглдеть намного лучше)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

и лучше сделай еще один файл cpp и туда реализацию перекинь) и имя файлу лучше придумать по лучше

{
if (a.size() != n) return {};

int left = 0;
int right = n-1;
while(left<right)
{
if (a[left]+a[right]<c) {++left; continue;}
if (a[left]+a[right]>c) {--right; continue;}
if (a[left]+a[right]==c) return {true, a[left], a[right]};
}

return {};
}

92 changes: 90 additions & 2 deletions task_01/src/test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,93 @@
#include <vector>
#include <gtest/gtest.h>
#include "solution.hpp"

TEST(Test, Simple) {
ASSERT_EQ(1, 1); // Stack []
class TwoSumTest : public ::testing::Test {
protected:
void SetUp() override {}
void TearDown() override {}
};

TEST_F(TwoSumTest, BasicCase) {
vector<int> nums = {2, 7, 11, 15};
int target = 9;
Result res = Solve(nums.size(), target, nums);

EXPECT_TRUE(res.finded);
EXPECT_EQ(res.left, 2);
EXPECT_EQ(res.right, 7);
}

TEST_F(TwoSumTest, NoSolution) {
vector<int> nums = {1, 2, 3, 4};
int target = 8;
Result res = Solve(nums.size(), target, nums);

EXPECT_FALSE(res.finded);
}

TEST_F(TwoSumTest, AllElementsSame) {
vector<int> nums = {5, 5, 5, 5};
int target = 10;
Result res = Solve(nums.size(), target, nums);

EXPECT_TRUE(res.finded);
EXPECT_EQ(res.left, 5);
EXPECT_EQ(res.right, 5);
}

TEST_F(TwoSumTest, NegativeNumbers) {
vector<int> nums = {-5, -3, 0, 1, 4};
int target = -2;
Result res = Solve(nums.size(), target, nums);

EXPECT_TRUE(res.finded);
EXPECT_EQ(res.left, -3);
EXPECT_EQ(res.right, 1);
}

TEST_F(TwoSumTest, EmptyArray) {
vector<int> nums = {};
int target = 5;
Result res = Solve(nums.size(), target, nums);

EXPECT_FALSE(res.finded);
}

TEST_F(TwoSumTest, SingleElementArray) {
vector<int> nums = {5};
int target = 5;
Result res = Solve(nums.size(), target, nums);

EXPECT_FALSE(res.finded);
}

TEST_F(TwoSumTest, TargetTooSmall) {
vector<int> nums = {10, 20, 30};
int target = 5;
Result res = Solve(nums.size(), target, nums);

EXPECT_FALSE(res.finded);
}

TEST_F(TwoSumTest, TargetTooLarge) {
vector<int> nums = {1, 2, 3};
int target = 10;
Result res = Solve(nums.size(), target, nums);

EXPECT_FALSE(res.finded);
}

TEST_F(TwoSumTest, MultipleSolutions) {
vector<int> nums = {1, 2, 3, 4, 5, 6};
int target = 7;
Result res = Solve(nums.size(), target, nums);

EXPECT_TRUE(res.finded);
EXPECT_EQ(res.left + res.right, target);
}

int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
3 changes: 3 additions & 0 deletions task_02/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#include <iostream>
#include <vector>
using namespace std;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

второй задачи нет(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я не до конца поняла, нужно было её делать или нет, т.к. в исходном файле уже было решение этой задачи

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

там реализация наивная и не оптимальная) нужно написать заново не на stack'е



int main() { return 0; }
6 changes: 5 additions & 1 deletion task_03/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#include <iostream>
#include <vector>
using namespace std;

int main() { return 0; }


int main() { return 0; }
23 changes: 23 additions & 0 deletions task_03/src/solution.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once
#include <vector>
#include <stack>

using namespace std;

inline vector<int> DailyTemp(const vector<int>& temp){
int n = temp.size();
vector<int> res(n, 0);
stack<int> s;

for(int i = 0; i<n; ++i)
{
while(!s.empty() && temp[i]>temp[s.top()])
{
int prev_d = s.top();
s.pop();
res[prev_d] = i - prev_d;
}
s.push(i);
}
return res;
}
46 changes: 42 additions & 4 deletions task_03/src/test.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@

#include <gtest/gtest.h>
#include <vector>
#include "solution.hpp"

using namespace std;

TEST(DailyTempTest, BasicTest) {
vector<int> temps = {73, 74, 75, 71, 69, 72, 76, 73};
vector<int> expected = {1, 1, 4, 2, 1, 1, 0, 0};
EXPECT_EQ(DailyTemp(temps), expected);
}

TEST(DailyTempTest, NoWarming) {
vector<int> temps = {80, 75, 70, 65, 60};
vector<int> expected = {0, 0, 0, 0, 0};
EXPECT_EQ(DailyTemp(temps), expected);
}

#include "topology_sort.hpp"
TEST(DailyTempTest, ImmediateWarming) {
vector<int> temps = {50, 60, 70, 80, 90};
vector<int> expected = {1, 1, 1, 1, 0};
EXPECT_EQ(DailyTemp(temps), expected);
}

TEST(DailyTempTest, EmptyInput) {
vector<int> temps = {};
vector<int> expected = {};
EXPECT_EQ(DailyTemp(temps), expected);
}

TEST(TopologySort, Simple) {
ASSERT_EQ(1, 1); // Stack []
TEST(DailyTempTest, SingleDay) {
vector<int> temps = {100};
vector<int> expected = {0};
EXPECT_EQ(DailyTemp(temps), expected);
}

TEST(DailyTempTest, MixedWarming) {
vector<int> temps = {30, 40, 20, 25, 35, 10};
vector<int> expected = {1, 0, 1, 1, 0, 0};
EXPECT_EQ(DailyTemp(temps), expected);
}

int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Loading
Loading