|
Първо контролно след НОИ, 2006 г., задача Traverse
Уловието на задачата може да прочетете
тук.
Решение
Решението на тази задача се базира на няколко наблюдения:
1.
Първото задраскано число за 1-вото обхождане в
права посока е 2N-1.
2.
След като го задраскаме, интервала [0, 2N]се разбива на 2
по-малки съответно: [0, 2N-1]и
[2N-1,2N]
3.
Обхождането на масива се разбива на две равни части
и важат следните твърдения:
a.
При j-то обхождане на
интервала [0, 2N-1]в права посока се
задраскват толкова числа, колкото при (j-1)-то обхождане на
интервала [2N-1, 2N]в
обратна посока.
b.
При j-то обхождане на
интервала [0, 2N-1]в обратна посока се
задраскват толкова числа, колкото при j-то обхождане на интервала [2N-1, 2N] в права посока.
Твърденията 3а и 3b могат лесно да се
докажат като се използва индукция.
Тези наблюдения водят до следните зависимости за броя числа задраскани при
j-то обхождане на интервал с дължина 2i в права или
обратна посока (0 – обозначава права
посока, 1 – обратна посока):
Ако
j е равно на 0,
opt[i][j][0] = 1 +
opt[i-1][j][0],
в противен случай
opt[i][j][0]=
opt[i-1][j-1][1] +
opt[i-1][j][0]
opt[i][j][1]
=
opt[i-1][j][1] +
opt[i-1][j][0]
След като се изчисли масива
opt[][][] по показания начин, ще може за всяка позиция да се каже числото,
което стои на тази позиция в списъка и посоката на обхождане, при която то е
задраскано. Остава да се разбере кое точно е числото. Функцията
f(int i, int j, int k,
num
s) прави точно това, като единственото, което решава е дали числото е
преди средата или след средата, като това става с малка проверка в масива
opt[][][]
(използва се правило 3). След като се провери това се модифицира числото
s (вади се първата половина от обхождането ако търсеното число е във
втората) и се извиква функцията за съответната част. Върнатата стойност се модифицира, за да се получи верният отговор за
голямата редица.
Забележка: налага се използването на дълги числа (използва се
структура
num).
Примерна реализация
/*
FROM: 2006 K1
PROB: traverse
KEYW: decoding, dynamical programming
*/
#include <cstdio>
#include <cassert>
#include <cstring>
const int MAXN = 1 << 10;
const int MAXT = 1 << 9;
const int MAX = 1 << 5;
const int BASE = 1000000000;
struct num {
int a[MAX];
num () {for (int i = 0; i < MAX; ++i) a[i] = 0;}
num (const num &b) {for (int i = 0; i < MAX; ++i) a[i] = b.a[i];}
num (int b) {for (int i = 0; i < MAX; ++i) a[i] = 0; a[0] = b;}
num& operator = (const num &b) {
for (int i = 0; i < MAX; ++i) a[i] = b.a[i];
return *this;
}
bool operator <= (const num &b) {
int i;
for (i = MAX-1; i > 0 && a[i] == b.a[i]; --i);
return a[i] <= b.a[i];
}
bool operator == (const num &b) {
int i;
for (i = 0; i < MAX && a[i] == b.a[i]; ++i);
return i == MAX;
}
num& operator += (const num &b) {
int pr = 0;
for (int i = 0; i < MAX; ++i) {
a[i] += b.a[i] + pr;
pr = a[i] / BASE;
a[i] %= BASE;
}
assert (!pr);
return *this;
}
num operator + (const num &b) const {
num ret (*this);
ret += b;
return ret;
}
num& operator -= (const num &b) {
for (int i = 0; i < MAX - 1; ++i) {
if (a[i] < b.a[i]) {
a[i] += BASE;
--a[i+1];
}
a[i] -= b.a[i];
}
assert (a[MAX-1] >= 0);
return *this;
}
num operator - (const num &b) const {
num ret (*this);
ret -= b;
return ret;
}
void scan () {
char buf[MAX*9];
fgets (buf, sizeof (buf), stdin);
int len = strlen (buf) - 1;
int fid = len / 9;
for (int i = 0; i < MAX; ++i) a[i] = 0;
for (int i = 0; i < len; ++i) {
if (!((len - i) % 9)) --fid;
a[fid] *= 10;
a[fid] += buf[i] - '0';
}
}
void prnt (bool b = 1) const {
int i;
for (i = MAX-1; i > 0 && !a[i]; --i);
printf ("%d", a[i]);
for (--i; i >= 0; --i) printf ("%09d", a[i]);
if (b) printf ("\n");
}
};
int N, K;
num pow2[MAXN];
num opt[MAXN][MAXT][2];
num f (int i, int j, int k, num s) {
if (j == 0 && k == 0 && s == 0) return pow2[i-1];
if (j == 0 && k == 0)
return pow2[i-1] + f (i - 1, j, k, s - 1);
if (k == 0) {
if (opt[i-1][j-1][1] <= s) {
s -= opt[i-1][j-1][1];
return pow2[i-1] + f (i - 1, j, 0, s);
} else
return pow2[i-1] - f (i - 1, j - 1, 1, s);
} else {
if (opt[i-1][j][1] <= s) {
s -= opt[i-1][j][1];
return pow2[i-1] - f (i - 1, j, 0, s);
} else
return pow2[i-1] + f (i - 1, j, 1, s);
}
}
int main () {
scanf ("%d", &N);
int i, j, k;
pow2[0] = 1;
for (i = 1; i <= N; ++i) pow2[i] = pow2[i-1] + pow2[i-1];
for (i = 1; i <= N; ++i)
for (j = 0;; ++j) {
opt[i][j][0] = (j ? opt[i-1][j-1][1] : 1) + opt[i-1][j][0];
opt[i][j][1] = opt[i-1][j][1] + opt[i-1][j][0];
if (opt[i][j][1] == 0) break;
}
num crnt;
scanf ("%d\n", &K);
for (i = 0; i < K; ++i) {
crnt.scan ();
crnt -= 1;
for (j = 0;; ++j)
for (k = 0; k < 2; ++k)
if (opt[N][j][k] <= crnt)
crnt -= opt[N][j][k];
else {
f (N, j, k, crnt).prnt ();
goto nxti;
}
nxti:;
}
return 0;
}
Автор: Искрен Чернев
обратно към текущ брой
|