Dynamic Programming To calculate the combinations

Problem Detail: This is a problem from a past contest at topcoder : Problem. Its solution is given here : Solution [Scroll Down to Penguin Emperor] I am unable to understand how the section with subheading “Combinations are associative” works. What is actually going on in this step ? How is the convolution property being used ? Or the Associative property of Combination ? How is the exponentiation squaring being performed ? EDIT Problem Statement You are Given N cities numbered from 0 to N-1 in a circular order. You are currently at city index 0. On first day you can move from your your current city with index i to a city with index j such that

j = (i+N-1)MOD N  or j = (i+1)MOD N   

On second day you can move from your your current city with index i to a city with index j such that

j = (i+N-2)MOD N  or j = (i+2)MOD N   

and so on. So on Day x, you can move from your your current city with index i to a city with index j such that

j = (i+N-(X MOD N))MOD N  or j = (i+(X MOD N))MOD N  

You have to travel on each day. Given the number of Days M, find the total number of ways, you can travel starting from city index 0 such that at the end of M days you are back to city index 0 , modulo 1000000007. The solution given is as following :

final int MOD = 1000000007; // Discrete convolution: int[] combine(int[] A, int[] B) {     int n = A.length;     int[] C = new int[n];     // Skipping when B[i] = 0, is a key optimization:     for (int i=0; i<n; i++) if (B[i] != 0) {         for (int j=0; j<n; j++) {             int k = (j - i + n) % n;             C[k] += (int)( (A[j]*(long)B[i]) % MOD );             if (C[k] >= MOD) {                 C[k] -= MOD;             }         }     }     return C; } // Exponentiation by squaring, for our convolution operation: int[] power(int[] A, long x) {     int n = A.length;     int[] R = new int[n];     R[0] = 1;     while (x > 0) {         if ( (x & 1) != 0) {             R = combine(R, A);         }         A = combine(A, A);         x >>= 1;     }     return R; }  public int countJourneys(int numCities, long  daysPassed) {     // Generate R and Q:     int[] Q;     int[] R = new int[numCities];     R[0] = 1;     Q = R;     for (int i=1; i<=numCities; i++) {         //B holds T[i]         int[] B = new int[numCities];         B[i % numCities] = 1;         B[numCities - i] = 1;          R = combine(R, B);          if (i == daysPassed % numCities) {             Q = R;         }     }     R = power(R, daysPassed / numCities);     R = combine(R, Q);     return R[0]; } 

countJourneys(N, M) is called to get the answer.

Asked By : Kyuubi

Answered By : Yuval Filmus

The idea is to use dynamic programming. For each day $k$, we find the number of ways (modulo $p = 1000000007$) to get from $0$ to $x$ for each $x in {1,ldots,N}$. Given the array $A_k$ for day $k$, the array $A_{k+1}$ for day $k+1$ is given by $$ A_{k+1}(x) = A_k(x-k-1) + A_k(x+k+1), $$ where all indices are modulo $N$. The actual computation is done modulo $p$. In your case, it seems that $M gg N$, and then this method is a bit slow. That’s why we want to use convolution. There is another way to describe the calculation above. Consider formal variables $x_0,ldots,x_{N-1}$ that satisfy the rule $x_i x_j = x_{i+j pmod{N}}$ (for concreteness, we could take $x_j = e^{2pi i (j/N)}$; but we could also just treat them as formal variables.) We represent the array $A_k$ in the form $$ A_k = sum_{i=0}^{N-1} A_k(i) x_i. $$ We have $A_0 = x_0$ and $A_{k+1} = (x_{k+1} + x_{-k-1}) A_k$ (all indices modulo $N$). This kind of multiplication is known as convolution, and it satisfies all the usual rules of multiplication (it is commutative and associative). When $M$ is big, we want to take advantage of the fact that $A_k = A_{k pmod{N}}$. So $$ A_k = prod_{i=1}^N (x_i + x_{-i})^{lceil (k-i+1)/N rceil} A_0. $$ The idea now is to compute $(x_i + x_{-i})^d$ using repeated squaring, which you can look up. For example, to compute $X^{10}$ we would compute $X,X^2,X^4,X^8$ and multiply $X^2$ and $X^8$ (more generally, we look at the binary expansion of the exponent). Given all the factors, we multiply everything together and then look at the coefficient of $x_0$. Since we only want the result modulo $p$, we do all the computations modulo $p$.
Best Answer from StackOverflow

Question Source : http://cs.stackexchange.com/questions/13039

Leave a Reply