diff --git a/arithmetic_progressions/Makefile b/arithmetic_progressions/Makefile index 47ae4b8..0e68759 100644 --- a/arithmetic_progressions/Makefile +++ b/arithmetic_progressions/Makefile @@ -7,8 +7,8 @@ all: ap %.o: %.c $(CC) -o $@ -c $^ $(CFLAGS) -ap: arithmetic_progressions.c segtree.o +ap: arithmetic_progressions.c itree.o $(CC) -o $@ $^ $(CFLAGS) clean: - rm -f segtree.o ap + rm -f segtree.o itree.o ap diff --git a/arithmetic_progressions/ap_all.c b/arithmetic_progressions/ap_all.c new file mode 100644 index 0000000..813d47f --- /dev/null +++ b/arithmetic_progressions/ap_all.c @@ -0,0 +1,522 @@ +#ifndef ITREE_H +#define ITREE_H + +/* + * Simple integer interval tree implementation + * Author: Titouan Rigoudy + */ + +struct itree { + int i; + int j; + long v; + struct itree *left; + struct itree *right; +}; + +// Describes the start or end of an interval +struct ievent { + int i; + long v; // i.v for interval start, -i.v for interval end +}; + +struct itree *itree_new(int i, int j, long v); + +void itree_free(struct itree *it); + +void itree_print(struct itree *it); + +void itree_add(struct itree *it, int i, int j, long v); + +// Flatten the tree into a list of events representing +// interval start and end points +int itree_flatten(struct itree *it, struct ievent **events); + +#endif +/* + * Simple integer interval tree implementation + * Kept sorted by interval starting point + * Not balanced + * Author: Titouan Rigoudy + */ + +#include +#include + +#define EVENTS_MIN_SIZE 1024 + +struct itree *itree_new(int i, int j, long v) { + struct itree *it = malloc(sizeof *it); + if (!it) { + return NULL; + } + it->i = i; + it->j = j; + it->v = v; + it->left = NULL; + it->right = NULL; + return it; +} + +void itree_free(struct itree *it) { + if (!it) { + return; + } + itree_free(it->left); + itree_free(it->right); + free(it); +} + +void itree_print(struct itree *it) { + if (!it) { + puts("NULL itree"); + } + printf("itree{i: %d, j:%d, v:%ld}\n", it->i, it->j, it->v); + puts("Left:"); + if (it->left) { + itree_print(it->left); + } + puts("Right:"); + if (it->right) { + itree_print(it->right); + } +} + +void itree_add(struct itree *it, int i, int j, long v) { + if (!it) { + fputs("Adding to null interval tree", stderr); + return; + } + + if (it->i == i && it->j == j) { + it->v += v; + return; + } + + if (i < it->i || (i == it->i && j <= it->j)) { + if (it->left) { + itree_add(it->left, i, j, v); + } else { + it->left = itree_new(i, j, v); + } + return; + } + + if (it->right) { + itree_add(it->right, i, j, v); + } else { + it->right = itree_new(i, j, v); + } +} + +// For use in qsort +int ieventcmp(const void *event1, const void *event2) { + const struct ievent *ev1 = event1; + const struct ievent *ev2 = event2; + return ev1->i - ev2->i; +} + +// For debugging purposes +void ievent_print(const struct ievent ev) { + printf("ievent{i:%d, v:%ld}\n", ev.i, ev.v); +} + +// Recursively add events from it to *events +// *events is of size *n, *i is the current number of events in *events +// Returns 0 on success, 1 on error +int itree_flatten_aux( + struct itree *it, struct ievent **events, int *n, int *i) { + + if (!it) { + return 0; + } + + int err = itree_flatten_aux(it->left, events, n, i); + if (err) { + return err; + } + free(it->left); + it->left = NULL; + + if (*i == *n) { + // grow events + *n = *n * 2 + 2; // just in case n was 0 + *events = realloc(*events, *n); + if (*events == NULL) { + return 1; // signal error + } + } + if (it->v != 0) { + // Add interval start event + (*events)[*i].i = it->i; + (*events)[*i].v = it->v; + (*i)++; + // Add interval end event + (*events)[*i].i = it->j; + (*events)[*i].v = -it->v; + (*i)++; + } + + err = itree_flatten_aux(it->right, events, n, i); + if (err) { + return err; + } + free(it->right); + it->right = NULL; + + it->v = 0; + + return 0; +} + +// Flatten the tree into a list of events representing +// interval start and end points +int itree_flatten(struct itree *it, struct ievent **events) { + int n = EVENTS_MIN_SIZE; + *events = malloc(n * sizeof(**events)); + if (*events == NULL) { + return 0; + } + int i = 0; + int err = itree_flatten_aux(it, events, &n, &i); + if (err) { + *events = NULL; + return 0; + } + qsort(*events, i, sizeof(**events), ieventcmp); + return i; +} +// https://www.hackerrank.com/challenges/arithmetic-progressions + +#include +#include +#include +#include +#include +#include + +#define INTS_START_SIZE 8 +#define INTS_DELIM " \t\n" +#define BUF_SIZE 10000000 +#define LINE_SIZE 16 +#define BIG_MOD 1000003 + +/* Arithmetic modulo BIG_MOD */ + +long INVERSES[BIG_MOD]; + +void calc_inverses() { + long tmp[BIG_MOD - 2]; + long acc = 2; // powers of 2 will run the full range of the set + int i; + for (i = 0; i < BIG_MOD - 2; i++) { + tmp[i] = acc; + acc = (acc * 2) % BIG_MOD; + } + INVERSES[0] = 0; + INVERSES[1] = 1; + for (i = 0; i < BIG_MOD - 2; i++) { + INVERSES[tmp[i]] = tmp[BIG_MOD - 3 - i]; + } +} + +long pow_mod(long a, long b, long mod, long acc) { + long powa = a % mod; + while (b > 0) { + if (b & 1) { + acc = (acc * powa) % mod; + } + powa = (powa * powa) % mod; + b >>= 1; + } + return acc; +} + +long factorial_mod(long n, long mod, long acc) { + int i; + for (i = 2; i <= n; i++) { + acc = (acc * i) % mod; + } + return acc; +} + +/* The actual logic */ + +struct adp { + long a; + long d; + long p; + long psum; // Cumulative sum of p values + long dpowp; // d ^ p + long dprod; // Cumulative product of d values + long dpowprod; // Cumulative product of d ^ p values +}; + +/* Read space-separated integers */ +size_t read_ints(char *line, int **ints) { + if (line == NULL) { + return 0; + } + size_t size = INTS_START_SIZE; + *ints = malloc(INTS_START_SIZE * sizeof *ints); + if (*ints == NULL) { + return 0; + } + + char *tok = line; + char *numend; + size_t i = 0; + long tmp; + while (*tok != '\n' && *tok != '\0') { + if (isspace(*tok)) { + tok++; + } else if (isdigit(*tok)) { + tmp = strtol(tok, &numend, 10); + if (i == size) { + // If we have filled array, double its size + size *= 2; + *ints = realloc(*ints, size * sizeof *ints); + if (*ints == NULL) { + return 0; + } + } + // store in array + (*ints)[i] = tmp; + i++; + tok = numend; + } else { + free(*ints); + *ints = NULL; + return 0; + } + } + return i; +} + +int read_adp(char *buf, int n, struct adp *adp) { + int *ints; + size_t ints_len; + + int i; + char *line; + long dpowp; + long psum = 0; + long dprod = 1; + long dpowprod = 1; + for (i = 0; i < n; i++) { + line = strtok(NULL, "\n"); + ints_len = read_ints(line, &ints); + if (ints_len != 3) { + return 1; + } + adp[i].a = ints[0]; + adp[i].d = ints[1]; + adp[i].p = ints[2]; + + psum += adp[i].p; + adp[i].psum = psum; + + dpowp = pow_mod(adp[i].d, adp[i].p, BIG_MOD, 1); + adp[i].dpowp = dpowp; + + dprod = (dprod * adp[i].d) % BIG_MOD; + adp[i].dprod = dprod; + + dpowprod = (dpowprod * dpowp) % BIG_MOD; + adp[i].dpowprod = dpowprod; + + free(ints); + } + return 0; +} + +// Calculate sum of p and product of d^p for [i, j[ +void itree_mcd( + struct adp *adp, + struct itree *ptree, + int i, + int j, + long *k, + long *dprod) +{ + if (!ptree) { + return; + } + + bool intersects = (i < j) && (i < ptree->j) && (ptree->i < j); + + if (intersects && ptree->v > 0) { + // We need to calculate the sum of the p values + // and the product of the d^p at the same time + int start = i > ptree->i ? i : ptree->i; + int end = j < ptree->j ? j : ptree->j; + + *k += ptree->v * (end-start); + + // calculate the product of all d values in [start, end[ + long dtmp = adp[end-1].dprod; + if (i > 0) { + dtmp = (dtmp * INVERSES[adp[start-1].dprod]) % BIG_MOD; + } + + *dprod = pow_mod(dtmp, ptree->v, BIG_MOD, *dprod); + } + + if (j > ptree->i) { + // [i,j[ may intersect with children intervals to the right + itree_mcd(adp, ptree->right, i, j, k, dprod); + } + + // [i,j[ can always intersect with children intervals to the left + itree_mcd(adp, ptree->left, i, j, k, dprod); +} + +// Calculate and print for [i-1, j-1] +int min_const_diff( + int n, + struct adp *adp, + struct itree *ptree, + int i, + int j) +{ + if (i < 1 || j > n) { + return 1; + } + // k is initialized to the sum of initial p values in [i-1, j-1] + long k = adp[j-1].psum; + // dprod is initializaed to the product of d^p values in [i-1, j-1] + long dprod = adp[j-1].dpowprod; + if (i >= 2) { + k -= adp[i-2].psum; + dprod = (dprod * INVERSES[adp[i-2].dpowprod]) % BIG_MOD; + } + itree_mcd(adp, ptree, i-1, j, &k, &dprod); + long v = factorial_mod(k, BIG_MOD, dprod); + printf("%ld %ld\n", k, v); + return 0; +} + +void update_adp(struct adp *adp, struct ievent *events, int n) { + if (!events || n <= 0) { + return; + } + int i = 0; + int p = 0; + int psum = 0; + int dpowprod = 1; + while (true) { + // Compress all events at the same index into one + int start = events[i].i; + while (i < n && events[i].i == start) { + p += events[i].v; + i++; + } + + if (i == n) { + // The last event should close the last remaining interval + assert(p == 0); + break; + } + + int end = events[i].i; // next event + + // p is now constant on [start, end[ + for (int j = start; j < end; j++) { + adp[j].p += p; + + psum += p; + adp[j].psum += psum; + + adp[j].dpowp = pow_mod(adp[j].d, adp[j].p, BIG_MOD, 1); + + dpowprod = (dpowprod * adp[j].dpowp) % BIG_MOD; + adp[j].dpowprod = dpowprod; + } + } +} + +int ptree_size = 0; + +int add_powers(int n, struct adp *adp, struct itree *ptree, int i, int j, int v) { + if (i < 1 || j > n) { + return 1; + } + itree_add(ptree, i-1, j, v); + ptree_size++; + if (ptree_size * ptree_size >= n) { + struct ievent *events = NULL; + int n_events = itree_flatten(ptree, &events); + update_adp(adp, events, n_events); + free(events); + ptree_size = 0; + } + return 0; +} + +int handle_query(char *str, int n, struct adp *adp, struct itree *ptree) { + int *ints; + int ints_len = read_ints(str, &ints); + if (ints_len < 1) { + return 1; + } + int query_type = ints[0]; + if (query_type == 0) { + if (ints_len != 3) { + return 1; + } + return min_const_diff(n, adp, ptree, ints[1], ints[2]); + } + if (query_type == 1) { + if (ints_len != 4) { + return 1; + } + return add_powers(n, adp, ptree, ints[1], ints[2], ints[3]); + } + return 1; +} + +int main(int argc, char **argv) { + // Prep work + calc_inverses(); + + char *buf = malloc(BUF_SIZE * sizeof *buf); + if (buf == NULL) { + return 0; + } + fread(buf, BUF_SIZE, 1, stdin); + + char *line = strtok(buf, "\n"); + + char *end; + int n = strtol(line, &end, 10); + if ((!isspace(*end) && *end != '\0') || n < 1) { + return 0; + } + + struct adp *adp = malloc(n * sizeof *adp); + struct itree *ptree = itree_new(0, n, 0); + if (adp == NULL || ptree == NULL) { + return 0; + } + int err = read_adp(buf, n, adp); + if (err) { + return 0; + } + + line = strtok(NULL, "\n"); + int q = strtol(line, &end, 10); + if ((!isspace(*end) && *end != '\0') || q < 1) { + return 0; + } + + int i; + for (i = 0; i < q; i++) { + line = strtok(NULL, "\n"); + err = handle_query(line, n, adp, ptree); + if (err) { + return 0; + } + } + + return 0; +} diff --git a/arithmetic_progressions/arithmetic_progressions.c b/arithmetic_progressions/arithmetic_progressions.c index 37b5f10..64741f5 100644 --- a/arithmetic_progressions/arithmetic_progressions.c +++ b/arithmetic_progressions/arithmetic_progressions.c @@ -4,8 +4,10 @@ #include #include #include +#include +#include -#include "segtree.h" +#include "itree.h" #define INTS_START_SIZE 8 #define INTS_DELIM " \t\n" @@ -143,9 +145,9 @@ int read_adp(char *buf, int n, struct adp *adp) { } // Calculate sum of p and product of d^p for [i, j[ -void segtree_mcd( +void itree_mcd( struct adp *adp, - struct segtree *ptree, + struct itree *ptree, int i, int j, long *k, @@ -155,42 +157,39 @@ void segtree_mcd( return; } - // We need to calculate the sum of the p values - // and the product of the d^p at the same time + bool intersects = (i < j) && (i < ptree->j) && (ptree->i < j); - *k += ptree->v * (j-i); + if (intersects && ptree->v > 0) { + // We need to calculate the sum of the p values + // and the product of the d^p at the same time + int start = i > ptree->i ? i : ptree->i; + int end = j < ptree->j ? j : ptree->j; + + *k += ptree->v * (end-start); - // dtmp is the product of all d values in [i, j[ - if (ptree->v > 0) { - long dtmp = adp[j-1].dprod; + // calculate the product of all d values in [start, end[ + long dtmp = adp[end-1].dprod; if (i > 0) { - dtmp = (dtmp * INVERSES[adp[i-1].dprod]) % BIG_MOD; + dtmp = (dtmp * INVERSES[adp[start-1].dprod]) % BIG_MOD; } *dprod = pow_mod(dtmp, ptree->v, BIG_MOD, *dprod); } - int mid = (ptree->i + ptree->j) / 2; - - if (j <= mid) { - segtree_mcd(adp, ptree->left, i, j, k, dprod); - return; - } - - if (i >= mid) { - segtree_mcd(adp, ptree->right, i, j, k, dprod); - return; + if (j > ptree->i) { + // [i,j[ may intersect with children intervals to the right + itree_mcd(adp, ptree->right, i, j, k, dprod); } - // i < mid && j > mid - segtree_mcd(adp, ptree->left, i, mid, k, dprod); - segtree_mcd(adp, ptree->right, mid, j, k, dprod); + + // [i,j[ can always intersect with children intervals to the left + itree_mcd(adp, ptree->left, i, j, k, dprod); } // Calculate and print for [i-1, j-1] int min_const_diff( int n, struct adp *adp, - struct segtree *ptree, + struct itree *ptree, int i, int j) { @@ -205,70 +204,70 @@ int min_const_diff( k -= adp[i-2].psum; dprod = (dprod * INVERSES[adp[i-2].dpowprod]) % BIG_MOD; } - segtree_mcd(adp, ptree, i-1, j, &k, &dprod); + itree_mcd(adp, ptree, i-1, j, &k, &dprod); long v = factorial_mod(k, BIG_MOD, dprod); printf("%ld %ld\n", k, v); return 0; } -// Flatten tree and add it to the adp array -void segtree_flatten(struct segtree *ptree, struct adp *adp, int n) { - if (!adp || !ptree) { +void update_adp(struct adp *adp, struct ievent *events, int n) { + if (!events || n <= 0) { return; } + int i = 0; + int p = 0; + int psum = 0; + int dpowprod = 1; + while (true) { + // Compress all events at the same index into one + int start = events[i].i; + while (i < n && events[i].i == start) { + p += events[i].v; + i++; + } - segtree_flatten(ptree->left, adp, n); - segtree_free(ptree->left); - ptree->left = NULL; - - segtree_flatten(ptree->right, adp, n); - segtree_free(ptree->right); - ptree->right = NULL; - - if (ptree->v != 0) { - for (int k = ptree->i; k < ptree->j && k < n; k++) { - adp[k].p += ptree->v; + if (i == n) { + // The last event should close the last remaining interval + assert(p == 0); + break; } - } + + int end = events[i].i; // next event - ptree->v = 0; -} + // p is now constant on [start, end[ + for (int j = start; j < end; j++) { + adp[j].p += p; -void update_adp(struct adp *adp, int n) { - int psum = 0; - int dpowprod = 1; - for (int i = 0; i < n; i++) { - psum += adp[i].p; - adp[i].psum = psum; + psum += p; + adp[j].psum += psum; - adp[i].dpowp = pow_mod(adp[i].d, adp[i].p, BIG_MOD, 1); + adp[j].dpowp = pow_mod(adp[j].d, adp[j].p, BIG_MOD, 1); - dpowprod = (dpowprod * adp[i].dpowp) % BIG_MOD; - adp[i].dpowprod = dpowprod; + dpowprod = (dpowprod * adp[j].dpowp) % BIG_MOD; + adp[j].dpowprod = dpowprod; + } } } int ptree_size = 0; -int add_powers(int n, struct adp *adp, struct segtree *ptree, int i, int j, int v) { +int add_powers(int n, struct adp *adp, struct itree *ptree, int i, int j, int v) { if (i < 1 || j > n) { return 1; } - //segtree_print(ptree); - //printf("Adding %ld to [%d,%d]\n", v, i, j); - segtree_add(ptree, i-1, j, v); + itree_add(ptree, i-1, j, v); ptree_size++; if (ptree_size * ptree_size >= n) { - //puts("Flattening the tree"); - segtree_flatten(ptree, adp, n); - update_adp(adp, n); + struct ievent *events = NULL; + int n_events = itree_flatten(ptree, &events); + update_adp(adp, events, n_events); + free(events); ptree_size = 0; } - //segtree_print(ptree); return 0; } -int handle_query(char *str, int n, struct adp *adp, struct segtree *ptree) { +int handle_query(char *str, int n, struct adp *adp, struct itree *ptree) { int *ints; int ints_len = read_ints(str, &ints); if (ints_len < 1) { @@ -309,7 +308,7 @@ int main(int argc, char **argv) { } struct adp *adp = malloc(n * sizeof *adp); - struct segtree *ptree = segtree_new_simple(n); + struct itree *ptree = itree_new(0, n, 0); if (adp == NULL || ptree == NULL) { return 0; } diff --git a/arithmetic_progressions/itree.c b/arithmetic_progressions/itree.c new file mode 100644 index 0000000..ac4e1c7 --- /dev/null +++ b/arithmetic_progressions/itree.c @@ -0,0 +1,155 @@ +/* + * Simple integer interval tree implementation + * Kept sorted by interval starting point + * Not balanced + * Author: Titouan Rigoudy + */ + +#include +#include + +#include "itree.h" + +#define EVENTS_MIN_SIZE 1024 + +struct itree *itree_new(int i, int j, long v) { + struct itree *it = malloc(sizeof *it); + if (!it) { + return NULL; + } + it->i = i; + it->j = j; + it->v = v; + it->left = NULL; + it->right = NULL; + return it; +} + +void itree_free(struct itree *it) { + if (!it) { + return; + } + itree_free(it->left); + itree_free(it->right); + free(it); +} + +void itree_print(struct itree *it) { + if (!it) { + puts("NULL itree"); + } + printf("itree{i: %d, j:%d, v:%ld}\n", it->i, it->j, it->v); + puts("Left:"); + if (it->left) { + itree_print(it->left); + } + puts("Right:"); + if (it->right) { + itree_print(it->right); + } +} + +void itree_add(struct itree *it, int i, int j, long v) { + if (!it) { + fputs("Adding to null interval tree", stderr); + return; + } + + if (it->i == i && it->j == j) { + it->v += v; + return; + } + + if (i < it->i || (i == it->i && j <= it->j)) { + if (it->left) { + itree_add(it->left, i, j, v); + } else { + it->left = itree_new(i, j, v); + } + return; + } + + if (it->right) { + itree_add(it->right, i, j, v); + } else { + it->right = itree_new(i, j, v); + } +} + +// For use in qsort +int ieventcmp(const void *event1, const void *event2) { + const struct ievent *ev1 = event1; + const struct ievent *ev2 = event2; + return ev1->i - ev2->i; +} + +// For debugging purposes +void ievent_print(const struct ievent ev) { + printf("ievent{i:%d, v:%ld}\n", ev.i, ev.v); +} + +// Recursively add events from it to *events +// *events is of size *n, *i is the current number of events in *events +// Returns 0 on success, 1 on error +int itree_flatten_aux( + struct itree *it, struct ievent **events, int *n, int *i) { + + if (!it) { + return 0; + } + + int err = itree_flatten_aux(it->left, events, n, i); + if (err) { + return err; + } + free(it->left); + it->left = NULL; + + if (*i == *n) { + // grow events + *n = *n * 2 + 2; // just in case n was 0 + *events = realloc(*events, *n); + if (*events == NULL) { + return 1; // signal error + } + } + if (it->v != 0) { + // Add interval start event + (*events)[*i].i = it->i; + (*events)[*i].v = it->v; + (*i)++; + // Add interval end event + (*events)[*i].i = it->j; + (*events)[*i].v = -it->v; + (*i)++; + } + + err = itree_flatten_aux(it->right, events, n, i); + if (err) { + return err; + } + free(it->right); + it->right = NULL; + + it->v = 0; + + return 0; +} + +// Flatten the tree into a list of events representing +// interval start and end points +int itree_flatten(struct itree *it, struct ievent **events) { + int n = EVENTS_MIN_SIZE; + *events = malloc(n * sizeof(**events)); + if (*events == NULL) { + return 0; + } + int i = 0; + int err = itree_flatten_aux(it, events, &n, &i); + if (err) { + *events = NULL; + return 0; + } + qsort(*events, i, sizeof(**events), ieventcmp); + return i; +} diff --git a/arithmetic_progressions/itree.h b/arithmetic_progressions/itree.h new file mode 100644 index 0000000..a895057 --- /dev/null +++ b/arithmetic_progressions/itree.h @@ -0,0 +1,35 @@ +#ifndef ITREE_H +#define ITREE_H + +/* + * Simple integer interval tree implementation + * Author: Titouan Rigoudy + */ + +struct itree { + int i; + int j; + long v; + struct itree *left; + struct itree *right; +}; + +// Describes the start or end of an interval +struct ievent { + int i; + long v; // i.v for interval start, -i.v for interval end +}; + +struct itree *itree_new(int i, int j, long v); + +void itree_free(struct itree *it); + +void itree_print(struct itree *it); + +void itree_add(struct itree *it, int i, int j, long v); + +// Flatten the tree into a list of events representing +// interval start and end points +int itree_flatten(struct itree *it, struct ievent **events); + +#endif