Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
 Аватар для Cortas
223 / 150 / 79
Регистрация: 14.03.2016
Сообщений: 459

Оптимальная сортировка для средних массивов

03.11.2018, 15:57. Показов 2977. Ответов 55
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго, к сожалению не очень шарю в разных методах сортировки, потому хочу спросить, какую сортировку лучше выбрать для массива, длина которого не превышает 500'000 элементов, а содержимое лежит в пределе [0; 500'000].
Если кратко, то https://www.cyberforum.ru/cgi-bin/latex.cgi?1 \leq n \leq 500'000 и https://www.cyberforum.ru/cgi-bin/latex.cgi?0 \leq {a}_{i} \leq n
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
03.11.2018, 15:57
Ответы с готовыми решениями:

функция нахождения среднего из значений средних арифметических для пяти массивов произвольной длины (с комментариями)
Доброго времени суток, помогите пожалуйста написать функцию, которая будет искать среднее из значений средних арифметических для пяти...

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

Поиск и оптимальная сортировка в datagridview
имеется сортировка в datagridview но она слишком простая, может кто нибудь знает посложнее? Dim Col As...

55
1719 / 568 / 187
Регистрация: 12.03.2016
Сообщений: 2,169
04.11.2018, 16:47
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Cortas Посмотреть сообщение
кто вам мешает это сделать?
Не кто мешает, а где это сделать?
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
04.11.2018, 16:51
Цитата Сообщение от Cortas Посмотреть сообщение
массив массивов
Нет
По коду видно же, что это массив std::set. Он отличается от массива тем, что поиск по нему происходит гораздо быстрее: O(logN), а не O(n).
0
 Аватар для Cortas
223 / 150 / 79
Регистрация: 14.03.2016
Сообщений: 459
04.11.2018, 16:57  [ТС]
Manowar, вы серьезно? Первая же ссылка в гугле :/

Добавлено через 5 минут
New man, ну, все равно, я понимаю, что set, это не простой массив, а, насколько я знаю, структура, построенная на принципе деревьев, тем не менее для полного решения нам нужно ускорить все это дело. И, как я понимаю и как уже было озвучено, выход из этого будет кэширование результатов, однако что нам делать при изменении исходного массива в уже проверенном диапазоне? Пока что приходит на ум только удаление.
0
1719 / 568 / 187
Регистрация: 12.03.2016
Сообщений: 2,169
04.11.2018, 17:13
Цитата Сообщение от Cortas Посмотреть сообщение
я сказал, что это ООШ - открытая олимпиада школьников
Где вы об этом говорили? Может я что то пропустил?
0
 Аватар для Cortas
223 / 150 / 79
Регистрация: 14.03.2016
Сообщений: 459
04.11.2018, 17:17  [ТС]
Manowar, в этом сообщении
0
1719 / 568 / 187
Регистрация: 12.03.2016
Сообщений: 2,169
04.11.2018, 17:35
Cortas, извиняюсь, не заметил. Но тема от своего первоначального названия ушла далеко в другую плоскость. Не уже ли так было тяжело сразу дать ссылку на сайт и номер задания когда просили не раз. А задание интересное.
0
 Аватар для Cortas
223 / 150 / 79
Регистрация: 14.03.2016
Сообщений: 459
04.11.2018, 19:43  [ТС]
Manowar, согласен, тема далеко ушла от названия, но в начале, я не предполагал развитие темы до текущего момента и наивно полагал, что оптимизации поиска элемента будет достаточно.

Добавлено через 1 час 29 минут
Что-то я совсем туплю и не могу придумать оптимального способа запоминать результат.
Пока что есть только две мысли: одна не оптимальна, вторая требует слишком много памяти.
1. пихать в массив диапазон и ответ для него. Постоянно придется проходить по нему, при изменении осн. массива вообще дичь.
2. создать двумерный массив, где первый индекс начало, второй конец диапазона. Вот только весить такое чудо при длине исходного в 500'000 будет https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{{500'000}^{2} * 4 }{{1024}^{3}} \approx 930 GB!

Добавлено через 30 минут
Точно, можно же использовать map<make_pair<uint, uint>, uint> не знаю на сколько будет поиск по такому map'у, но это всяко лучше, чем то, что я смог придумать до этого
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
04.11.2018, 23:06
Cortas, вот это даёт больше баллов но проходит меньше тестов.
Можешь как-нибудь совместить со старым вариантом (например, не строя lookup, если максимум всех элементов <10).

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
 
 
using t_positions = std::set<unsigned>;
 
 
std::vector<t_positions> prepare(const std::vector<unsigned>& base)
{
    std::vector<t_positions> res(base.size() + 1);
    for (size_t i = 0; i < base.size(); ++i)
        res[base[i]].insert(i);
    return res;
}
 
struct lookup
{
    unsigned left;
    unsigned right;
    bool operator<(lookup r) const { return left < r.left || (left == r.left && right < r.right); }
    bool operator==(lookup r) const { return left == r.left && right == r.right; }
 
    unsigned distance(unsigned pos) const
    {
        if (left <= pos && right >= pos)
            return 0;
        if (pos < left)
            return left - pos;
        return pos - right;
    }
 
    bool contains(const lookup in) const
    {
        return left <= in.left && right >= in.right;
    }
};
 
std::vector<std::set<lookup>> lookups;
 
 
 
void build_lookups(const std::vector<t_positions>& mapper)
{
    // clean up
    lookups.resize(mapper.size());
    for (auto& l : lookups)
    {
        if(l.empty())
            break;
        l.clear();
    }
 
 
    for (unsigned pos : mapper[0])
    {
        lookup look{pos, pos};
        lookups[0].insert(look);
    }
 
    for (unsigned i = 1; i < mapper.size(); ++i)
    {
        for (unsigned pos : mapper[i])
        {
            lookup closest;
            bool initialized = false;
            auto start = lookups[i - 1].lower_bound({ pos, pos });
            if(start!=lookups[i - 1].begin())
            {
                --start;
                start = lookups[i - 1].lower_bound({ start->left, start->left });
            }
            auto stop = lookups[i - 1].upper_bound({ pos, pos });
            for (auto iter = start; iter!=lookups[i-1].end(); ++iter)
            {
                const lookup prev = *iter;
                if (!initialized)
                {
                    closest = prev;
                    initialized = true;
                    if (iter == stop)
                        break;
                    continue;
                }
                const unsigned prev_dist = prev.distance(pos),
                               curr_dist = closest.distance(pos);
                if (prev_dist < curr_dist)
                {
                    closest = prev;
                    if (iter == stop)
                        break;
                    continue;
                }
                if (prev_dist == curr_dist && prev.right - prev.left < closest.right - closest.left)
                {
                    closest = prev;
                }
                if(iter==stop)
                    break;
            }
            if (initialized)
            {
                unsigned l = std::min(pos, closest.left);
                unsigned r = std::max(pos, closest.right);
                lookup adding{l, r};
                bool add = true;
 
                auto iter = lookups[i].lower_bound(adding);
                while(iter!= lookups[i].end() && iter->left<=adding.right)
                {
                    if(adding.contains(*iter))
                    {
                        add = false;
                        break;
                    }
                }
 
                if (add)
                    lookups[i].insert(adding);
            }
        }
        if (lookups[i].empty())
            break;
    }
}
 
void update_lookups(size_t upd_pos, unsigned val, const std::vector<t_positions>& mapper)
{
    for (unsigned i = val; i < mapper.size(); ++i)
    {
        if(lookups[i].empty())
            break;
        lookups[i].clear();
    }
    for (unsigned i = val; i < mapper.size(); ++i)
    {
        for (unsigned pos : mapper[i])
        {
            lookup closest;
            bool initialized = false;
            auto start = lookups[i - 1].lower_bound({ pos, pos });
            if (start != lookups[i - 1].begin())
            {
                --start;
                start = lookups[i - 1].lower_bound({ start->left, start->left });
            }
            auto stop = lookups[i - 1].upper_bound({ pos, pos });
            for (auto iter = start; iter != lookups[i - 1].end(); ++iter)
            {
                const lookup prev = *iter;
                if (!initialized)
                {
                    closest = prev;
                    initialized = true;
                    continue;
                }
                const unsigned prev_dist = prev.distance(pos),
                    curr_dist = closest.distance(pos);
                if (prev_dist < curr_dist)
                {
                    closest = prev;
                    continue;
                }
                if (prev_dist == curr_dist && prev.right - prev.left < closest.right - closest.left)
                {
                    closest = prev;
                }
                if (iter == stop)
                    break;
            }
            if (initialized)
            {
                unsigned l = std::min(pos, closest.left);
                unsigned r = std::max(pos, closest.right);
                lookup adding{ l, r };
                bool add = true;
 
                auto iter = lookups[i].lower_bound(adding);
                while (iter != lookups[i].end() && iter->left <= adding.right)
                {
                    if (adding.contains(*iter))
                    {
                        add = false;
                        break;
                    }
                }
 
                if (add)
                    lookups[i].insert(adding);
            }
        }
        if (lookups[i].empty())
            break;
    }
}
 
void replace(size_t pos, unsigned value, std::vector<unsigned>& arr, std::vector<t_positions>& mapper)
{
    unsigned old = arr[pos];
    if (old == value)
        return;
    arr[pos] = value;
 
    mapper[old].erase(pos);
    mapper[value].insert(pos);
 
    if (old == 0 || value == 0)
        build_lookups(mapper);
    else
        update_lookups(pos, std::min(old, value), mapper);
}
 
/// Both inclusive
bool is_set_contains(const t_positions& positions, size_t left, size_t right)
{
    if (positions.empty())
        return false;
    const auto iter = positions.lower_bound(left);
    if (iter == positions.end() || *iter > right)
        return false;
    return true;
}
 
bool check_look_up(unsigned val, lookup range, const std::vector<std::set<lookup>>& lookups)
{
    const std::set<lookup>& look = lookups[val];
    auto start = look.lower_bound(range);
    if(start!=look.begin())
    {
        --start;
        start = look.lower_bound({ start->left, start->left });
    }
    while(start!=look.end() && range.right>start->left)
    {
        if (range.contains(*start))
            return true;
        ++start;
    }
    return  false;
}
 
unsigned find_start(const std::vector<std::set<lookup>>& lookups, unsigned left, unsigned right)
{
    unsigned lower = 0, upper = right - left;
    lookup current{left, right};
    if (!check_look_up(0, current, lookups))
        return 0;
    if (check_look_up(upper, current, lookups))
        return upper;
    do
    {
        unsigned middle = (static_cast<unsigned long long>(lower) + upper) >> 1;
        bool contains_all_lower = check_look_up(middle, current, lookups);
        if (contains_all_lower)
            lower = middle + 1;
        else
            upper = middle;
    } while (upper - lower>5);
    return lower;
}
 
size_t first_lost(unsigned left, unsigned right, const std::vector<t_positions>& mapper)
{
    size_t start = find_start(lookups, left, right);
 
    for (size_t i = start; i < mapper.size(); ++i)
        if (!is_set_contains(mapper[i], left, right))
        {
            return i;
        }
    return mapper.size();
}
 
 
int main()
{
    size_t n;
    size_t number_of_commands;
    std::cin >> n;
    std::cin >> number_of_commands;
    std::vector<unsigned> arr(n);
    for (unsigned& v : arr)
        std::cin >> v;
    std::vector<t_positions> mapper = prepare(arr);
    build_lookups(mapper);
    std::vector<unsigned> prints;
    prints.reserve(number_of_commands);
    for (size_t i = 0; i < number_of_commands; ++i)
    {
        char cmd;
        std::cin >> cmd;
        if (cmd == '?')
        {
            size_t l, r;
            std::cin >> l >> r;
            prints.push_back(first_lost(l - 1, r - 1, mapper));
        }
        else
        {
            unsigned value;
            size_t pos;
            std::cin >> pos >> value;
            replace(pos - 1, value, arr, mapper);
        }
    }
 
    for (unsigned v : prints)
        std::cout << v << '\n';
}
0
 Аватар для Cortas
223 / 150 / 79
Регистрация: 14.03.2016
Сообщений: 459
05.11.2018, 10:30  [ТС]
New man, а можете в кратце описать свой алгоритм?
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
05.11.2018, 13:12
Для каждого числа n строим как можно более короткие последовательности, где есть все значения от 0 до n.

Потом для l,r узнаём число start, для которого есть такая подпоследовательность в [l,r]. Для этого использую двоичный поиск.
1
 Аватар для Cortas
223 / 150 / 79
Регистрация: 14.03.2016
Сообщений: 459
05.11.2018, 15:52  [ТС]
New man, я сделал немного иначе, правда в итоге программа прошла больше тестов, но заработала меньше баллов (24). В общем, я создал структуру, где лежит 2 числа диапазона и ответ для этого диапазона, взял ваш алгоритм по поиску числа. Далее я каждую команду запоминал, команда по поиску: диапазон, результат; команда по замене: первое число диапазона менял со стандартного (-1) на введенное. Если поступала команда по поиску, то я сначала проходил по массиву с историей и смотрел, есть ли там такой же диапазон или есть ли там изменения в нововведенном диапазоне. При выполнении первого условия просто выводил результат, при втором приходилось заново искать. Можно ли такую систему как-нибудь улучшить (если оно того стоит), например set'ом, просто я не до конца понимаю, как при поиске учесть след. условия:
C++
1
2
3
4
5
6
7
//curr_right == -1 означает, что были изменения, элемента под номером curr_left как было описано выше.
 
//выполнение этого условия значит, что в введенном диапазоне были изменения и в нем число надо искать заново
(curr_right == -1 && input_left <= curr_left && input_right >= curr_left) 
 
//а выполнение это означает, что нужно просто вывести результат
(curr_left == input_left && curr_right == input_right)
Добавлено через 3 минуты
код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <iostream>
#include <vector>
#include <set>
 
 
using t_positions = std::set<unsigned>;
 
std::vector<t_positions> prepare(const unsigned* arr, size_t size)
{
    std::vector<t_positions> res(size + 1);
    for(size_t i = 0; i < size; ++i)
        res[arr[i]].insert(i);
    return res;
}
 
 
void replace(size_t pos, unsigned value, unsigned* arr, std::vector<t_positions>& mapper)
{
    unsigned old = arr[pos];
    arr[pos] = value;
 
    mapper[old].erase(pos);
    mapper[value].insert(pos);
}
/// Both inclusive
bool is_set_contains(const t_positions& positions, size_t left, size_t right)
{
    if(positions.size() == 0)
        return false;
    auto iter = positions.lower_bound(left);
    if(iter == positions.end() || *iter > right)
        return false;
    return true;
}
 
size_t first_lost(size_t left, size_t right, const std::vector<t_positions>& mapper)
{
    for(size_t i = 0; i < mapper.size(); ++i)
        if(!is_set_contains(mapper[i], left, right))
            return i;
    return mapper.size();
}
 
struct history
{
    long left, right;
    long res;
    history() : left(-1), right(-1), res(-1) {}
};
 
int main()
{
    size_t n;
    size_t number_of_commands;
    std::cin >> n;
    std::cin >> number_of_commands;
 
    //std::vector<unsigned> arr(n);
    unsigned *arr = new unsigned[n];
    for(int i = 0; i < n; i++)
        std::cin >> arr[i];
    std::vector<t_positions> mapper = prepare(arr, n);
 
    //std::vector<history> hist(number_of_commands);
    history* hist = new history[number_of_commands];
 
    for(size_t i = 0; i < number_of_commands; ++i)
    {
        char cmd;
        std::cin >> cmd;
        if(cmd == '?')
        {
            size_t l, r, res;
            std::cin >> l >> r;
 
            long j = i - 1;
            while(
                ( j >= 0 ) &&
                !( hist[j].right == -1 && hist[j].left >= l && hist[j].left <= r ) &&
                !( hist[j].left == l && hist[j].right == r )
                )
            { j--; }
 
            if(j >= 0 && hist[j].res != -1)
                std::cout << hist[j].res << std::endl;
            else
            {
                res = first_lost(l - 1, r - 1, mapper);
                std::cout << res << std::endl;
                hist[i].left = l;
                hist[i].right = r;
                hist[i].res = res;
            }
        }
        else
        {
            unsigned value;
            size_t pos;
            std::cin >> pos >> value;
            replace(pos - 1, value, arr, mapper);
            hist[i].left = pos;
        }
    }
    delete[] hist;
    delete[] arr;
    return 0;
}
0
4 / 5 / 1
Регистрация: 28.06.2018
Сообщений: 29
07.11.2018, 01:01
А может не будем дружно решать задачу с еще идущей олимпиады?
btw, достаточно простую задачу
0
 Аватар для Cortas
223 / 150 / 79
Регистрация: 14.03.2016
Сообщений: 459
07.11.2018, 22:39  [ТС]
Цитата Сообщение от pogrammer Посмотреть сообщение
btw, достаточно простую задачу
Достаточно простую для решения на максимальный балл или просто так, чтобы работало?
0
4 / 5 / 1
Регистрация: 28.06.2018
Сообщений: 29
07.11.2018, 23:04
На максимальный
0
738 / 705 / 110
Регистрация: 29.05.2015
Сообщений: 4,356
08.11.2018, 06:08
Если я правильно понял задачу, зачем вообще сортировать исходный массив (ИМ)?

Очевидно, что если в ИМ будут присутствовать все числа 1-500000, то последовательность нигде не прерывается. Последовательность прервётся на том числе, которое будет первое отсутствовать в ИМ.

алгоритм:

1. создать булевый массив (БМ) размером 500 000, заполненный false
2. пройти по ИМ от начала до конца, заполняя БМ: БМ[число из ИМ] = true;
3. пройти по БМ, первый false будет означать отсутствие данного числа в ИМ и прерывание последовательности.
0
322 / 174 / 78
Регистрация: 09.10.2014
Сообщений: 809
08.11.2018, 12:41
Цитата Сообщение от alexu_007 Посмотреть сообщение
Если я правильно понял задачу, зачем вообще сортировать исходный массив (ИМ)?
В ходе обсуждения выяснилось, что есть доп. условия...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
08.11.2018, 12:41

Оптимальная сортировка без дублирования данных
Прошу помочь написать сортировку, которая позволит рационально расходовать память, избегая дублирования данных. Их может быть очень много....

Найти минимальное из средних арифметических 3 массивов.
Даны 3 массива по 28 элементов. Найти минимальное из средних арифметических для соответствующих элементов данных массивов. Напечатать...

Найти произведение средних арифметических элементов массивов X(n,m) и Y(n,m)
Найти произведение средних арифметических элементов массивов X(n,m) и Y(n,m). Pascal

Из средних значений двух массивов найти минимальное
Задание такое Определить минимальное значение Srmin = = min{Sr1, Sr2} среднего значения Sr1 массива xi и сред него значения Sr2...

Вычисление средних значений среди отрицательных чисел массивов
Даны 5 двумерных массивов A, B, C, D, E разной длины. Разработать процедуру ввода массивов и вычисления средних значений среди...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
56
Ответ Создать тему
Новые блоги и статьи
Особенности gif сгенерированных в Си
russiannick 21.10.2026
Рассмотрим генерацию gif файлов, которые в Си имеют свои особенности. Gif представляет собой графический формат популярный в интернете. Посмотрим на палитру. Gif файл сгенерированный в си отличается. . .
FPS контроллер с анимированными Y-Bot и 3D-звуком miniaudio (Box3D, Recast, BehaviorTree.CPP, ozz-animation, ImGui, OpenGL и SDL3)
8Observer8 18.10.2026
Содержание блога | English Version Полное мультиплатформенное руководство по стартовому проекту с FPS-контроллером игрока, анимированными роботами Y-Bot с 4-слойным блендингом, 3D-физикой Box3D,. . .
Локальный http сервер
Argus19 17.10.2026
Локальный http сервер На работе, на двух локальных компьютерах круглосуточно работают программы опроса приборов с записью в книги Excel,которые раз в сутки записываются на сервер компании. Т. к. . . .
Копировать Windows на новый диск на UEFI
damix 17.10.2026
https:/ / uriesk. wordpress. com/ 2016/ 11/ 01/ move-window-10-to-ssd-on-a-uefi-dualboot-system-with-basic-linux-tools/ TL DR Вставляем диск, а старый подключаем через переходник, грузимся с лайва. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru