LA 3516

3516 - Exploring Pyramids

 * http://acmicpc-live-archive.uva.es/nuevoportal/data/problem.php?p=3516

NEERC November 30, 2015 (page 7):
 * http://neerc.ifmo.ru/archive/2005/problems/problems.pdf

Summary
Find the number of rooted trees (ignoring rotations and chosen order of traversal) with the given order of eulerian traversal of the tree whose vertexes are marked with letters.

Explanation
The problem can be solved if we define function $$F(S)$$ - the number of trees whose traversal order gives the resutling string $$S$$. For a valid $$S: S[1] = S[n]$$.

The following recurrence relation holds for the function $$F(S)$$: $$F(S) = \sum_{i = 2..n} F(S[2..i - 1]) * F(S[i..n]), iff S[1] = S[i].$$


 * 1) include 
 * 2) include 

typedef long long ll_t;

char S[400]; int P[400][400]; ll_t R[400][400];

ll_t F(int l, int r) { int i; if (l > r) return 0; if (l == r) return (S[l] == S[r]); if (P[l][r]) return R[l][r]; P[l][r] = 1, R[l][r] = 0; for (i = l + 1; i <= r; i++) if (S[i] == S[l]) R[l][r] = (R[l][r] + F(l + 1, i - 1) * F(i, r)) % 1000000000; return R[l][r]; }

int main { while (scanf ("%s", S) == 1) { memset (P, 0, sizeof(P)); printf ("%lld\n", F(0, strlen(S) - 1)); } return 0; }

The quadratic optimization (step 1)
The code above could be optimized according to the minimization of the quadratic upper bound of the algorithm.

The following code does the same except that it's not necessary to set the initial traverse mark to zero, as it's required when dealing with multiple test cases:

1. Let's define the static global variable (stored in one register for example):

static int MarkNum = 0;

2. Let's increase the number of mark each time we run the test: MarkNum = MarkNum + 1;

3. Let's comment the unnecessary excess operation: // memset (P, 0, sizeof(P));

4. Let's change the following code: if (P[l][r]) return R[l][r]; P[l][r] = 1, R[l][r] = 0;

With the implementation of traverse mark counter it will look like: if (P[l][r] == MarkNum) return R[l][r]; P[l][r] = MarkNum, R[l][r] = 0;

5. Finally the optimized solution will be as follows (the standard C-language semantics is used):
 * 1) include 
 * 2) include 

typedef long long ll_t;

char S[400]; int P[400][400]; ll_t R[400][400];

static int MarkNum = 0; // mark number

ll_t F(int l, int r) { int i; if (l > r) return 0; if (l == r) return (S[l] == S[r]); if (P[l][r] == MarkNum) return R[l][r]; P[l][r] = MarkNum, R[l][r] = 0; for (i = l + 1; i <= r; i++) if (S[i] == S[l]) R[l][r] = (R[l][r] + F(l + 1, i - 1) * F(i, r)) % 1000000000; return R[l][r]; }

int main { memset (P, 0, sizeof(P)); while (scanf ("%s", S) == 1) { MarkNum = MarkNum + 1; printf ("%lld\n", F(0, strlen(S) - 1)); } return 0; }

The quadratic optimization (step 2)
The following code is well good, however, - do we avoid the quadratic $$n^2$$ upper bound even when the traverse array P is initialized to zero? To do that we can use the non-deterministic approach of randomly selecting the value of MarkNum variable: MarkNum = rand;

Before that of course we can make sure that the random generator is synchronized with system timer: time_t t; srand((unsigned) time(&t));

However this is unnecessary as, by default, any constant can be selected as the probability of number equality is 1/N, where N is the cardinality of the positive number in computer arithmetic. This probability is also divided by the memory size of the machine and the number of cycles it will run. Thus the probability of the equality, in fact, equals to zero.

Thus, the code will be shortened to:
 * 1) include 
 * 2) include 

typedef long long ll_t;

char S[400]; int P[400][400]; ll_t R[400][400];

static int MarkNum = 0; // mark number

ll_t F(int l, int r) { int i; if (l == r) return 1; if (P[l][r] == MarkNum) return R[l][r]; P[l][r] = MarkNum, R[l][r] = 0; for (i = l + 1; i <= r; i++) if (S[i] == S[l]) R[l][r] = (R[l][r] + F(l + 1, i - 1) * F(i, r)) % 1000000000; return R[l][r]; }

int main { while (scanf ("%s", S) == 1) { MarkNum = MarkNum + 1; printf ("%lld\n", F(0, strlen(S) - 1)); } return 0; }

The experimentation shows that the recursion repeated occurrence cannot be avoided. Thus it's required to use the mark array.

Input
ABABABA ABA

Output
5 1