Второ Българско
Онлайн Състезание, 24.10.2004
Анализ и примерна реализация на задача COL.
Ще разгледаме
четири подхода към задачата. Първите два са доста очевидни, а последният е оптималното решение на
проблема:
1) Най-лесният
начин да се справим със задачата е да заделим квадратна таблица DxD, в която
дапомним числото за всеки отделен район. На актуализация за район (x,y)
променяме директно клетката х,у в тази таблица, а на запитване четем пак
директно от нея.
Ограничението
за x,y обаче предполага заделяне на таблица 10 000 X 10 000, което прави 100
000 000 клетки. В С това ще означава 100 000 000 X 4 Bytes = 400 МB int-ове.
Memory Limit за тази задача е 64МB.
2) Друго лесно
решение е по-пестеливо откъм памет, но харчи прекомерно много време: да запазим
в масив всички актуализации и на всяко запитване да го сканираме целия за данни
относно клетката от запитването. Събираме всичко в една променлива и е
отговорът на запитването. Времева сложност O(n*m), като n*m = max 100 000 * 100 000,
което, разбира се, не може да се вмести във времевото ограничение.
3) Третият подход
е малко по-сложен. Нека масивът, в който си записваме входа е A. След прочитане
сортираме A. Сортираният А трябва да се състои от "блокове"
информация за един и същ район: примерно от i дo j в А има данни за район
(10,10), в позиция j+1 е единствената актуализация за (15,100), а от j+2 до k -
за (20,60). Събираме всичките жители от тези клетки в променлива S, след което
приравняваме информацията във всяка от тях на S.
За всяко
запитване правим двоично търсене в А за посочения район. Когато попаднем в
"блока" на този район, веднага имаме финалния брой жители в него.
4) За решение
на всички тестове трябва да се реализира т.нар. hash-таблица. Всъщност, тази задача е класически пример за
проблем, който изисква такава.
Какво е тя и
как се имплементира може да се прочете в почти всяка книга за алгоритми.
От самолет се
вижда, че броят на районите, за които евентуално ще постъпи някаква информация
е много малка част от общия им брой (10^5/10^8 = 0.1%), а самият той е съвсем «поносимо»
число. Следователно хеш-таблицата е наистина удачна.
Правим една
хеш-таблица със 'статична' част от 100 000 (а може и повече) елемента. При
всяка нова актуализация намираме двойката координати в таблицата и добавяме
там. Аналогично четем финалните резултати при отговаряне на запитване.
Тестовете на
задачата бяха ориентирани към 30т. за решения, сходни с 2), 60т. за такива,
сходни с 3) и 100т. за такива, сходни с 4). Възможно е обаче поради малко
по-либералния time-limit неоптимални решения да са взели 60 и
повече точки.
Следва
примерна реализация на четвърти подход на С.
/*
TASK:COL
LANG:C
*/
#include
<stdio.h>
#include
<stdlib.h>
#define
MAXN 100010
#define
MAXM 100010
typedef
struct ssq ssq;
struct
ssq {
int x, y, z;
ssq* next;
};
ssq
sq[MAXM];
int
f[MAXM];
int
n,m;
int
main () {
int i, cd, a, b, c, cnt = 0; ssq *p;
scanf("%d %d",&n,&m);
for (i = 1; i <= n; i++) {
scanf("%d %d
%d",&a,&b,&c);
cd = (a*b) % MAXM;
if (!f[cd]) {
sq[cd].x = a; sq[cd].y = b; sq[cd].z = c;
f[cd] = 1;
cnt++;
} else {
p = sq+cd;
if (p->x == a && p->y
== b) {
p->z += c;
continue;
}
while (p->next &&
((p->next)->x != a || (p->next)->y != b))
p = p->next;
if (!(p->next)) {
p->next = (ssq
*)malloc(sizeof(ssq));
p = p->next;
p->x = a; p->y = b; p->z = c;
p->next = 0;
} else {
p = p->next;
p->z += c;
}
}
}
for (i = 1; i <= m; i++) {
scanf("%d
%d",&a,&b);
cd = (a*b) % MAXM;
if (!f[cd]) {
printf("0\n");
} else {
p = sq+cd;
while (p && (p->x != a ||
p->y != b)) p = p->next;
if (p)
printf("%d\n",p->z);
else printf("0\n");
}
}
return 0;
}
Николай
Вълчанов, СМГ, valtchanov->mail.bg