3193. Count the Number of Inversions

Dare2Solve

Dare2Solve

3193. Count the Number of Inversions
SAMSUNG 49-Inch Odyssey G9
SAMSUNG 49-Inch Odyssey G9
Because earth is not flat

Intuition

The problem requires counting permutations of an array such that certain segments of the array have specified inversion counts. An inversion in an array is a pair of indices where the earlier index has a larger value than the later index. Given this, the goal is to find permutations of [0, 1, 2, ..., n - 1] that meet these inversion requirements for specified segments.

The core idea is to use dynamic programming to count the number of valid permutations while ensuring that the specified inversion counts are satisfied. We use a DP table where each entry dp[i][j] represents the number of ways to arrange the first i elements with exactly j inversions.

Approach

  1. Initialization: Create a 2D DP array dp where dp[i][j] stores the number of ways to arrange the first i elements of the permutation such that there are exactly j inversions. Also, create an array req to store the required inversion counts for each segment.
  2. Setup Requirements: Fill the req array with the given requirements. For each requirement [endi, cnti], set req[endi + 1] = cnti.
  3. Base Case: If there are no inversions required for the first segment, set dp[1][0] to 1 because there's only one way to arrange a single element with zero inversions.
  4. DP Transition: Iterate through each element from 2 to n and for each element, calculate the possible number of inversions. For each possible inversion count j up to 400 (as a heuristic upper bound), update the DP table by considering all positions where the new element can be inserted to create additional inversions.
  5. Handle Requirements: Ensure that only the DP states that meet the inversion requirements for each segment are updated and considered.
  6. Result: The final result will be stored in dp[n][req[n]], which gives the number of permutations of the entire array satisfying the inversion count for the final segment.

Complexity

Code

C++

class Solution {
public:
    int numberOfPermutations(int n, vector<vector<int>>& requirements) {
        vector<vector<int>> dp(n + 1, vector<int>(401, 0));
        vector<int> req(n + 1, -1);

        for (auto& r : requirements) {
            req[r[0] + 1] = r[1];
        }

        const int mod = 1e9 + 7;

        if (req[1] <= 0) {
            dp[1][0] = 1;
        }

        for (int i = 2; i <= n; i++) {
            for (int c = 0; c < i; c++) {
                for (int j = 0; j + c <= 400; j++) {
                    if (req[i] != -1 && j + c != req[i]) {
                        continue;
                    }
                    dp[i][j + c] += dp[i - 1][j];
                    dp[i][j + c] %= mod;
                }
            }
        }

        return dp[n][req[n]];
    }
};

Python

class Solution:
    def numberOfPermutations(self, n: int, requirements: List[List[int]]) -> int:
        dp = [[0] * 401 for _ in range(n + 1)]
        req = [-1] * (n + 1)
        
        for a, b in requirements:
            req[a + 1] = b
        
        if req[1] <= 0:
            dp[1][0] = 1
        
        for i in range(2, n + 1):
            for c in range(i):
                for j in range(401 - c):
                    if req[i] != -1 and j + c != req[i]:
                        continue
                    dp[i][j + c] += dp[i - 1][j]
                    dp[i][j + c] %= 1000000007
        
        return dp[n][req[n]]

Java

class Solution {
    public int numberOfPermutations(int n, int[][] requirements) {
        int[][] dp = new int[n + 1][401];
        int[] req = new int[n + 1];
        Arrays.fill(req, -1);

        for (int[] requirement : requirements) {
            int a = requirement[0];
            int b = requirement[1];
            req[a + 1] = b;
        }

        if (req[1] <= 0) {
            dp[1][0] = 1;
        }

        for (int i = 2; i <= n; i++) {
            for (int c = 0; c < i; c++) {
                for (int j = 0; j + c <= 400; j++) {
                    if (req[i] != -1 && j + c != req[i]) continue;
                    dp[i][j + c] += dp[i - 1][j];
                    dp[i][j + c] %= 1000000007;
                }
            }
        }

        return dp[n][req[n]];
    }
}

JavaScript

/**
 * @param {number} n
 * @param {number[][]} requirements
 * @return {number}
 */
var numberOfPermutations = function (n, requirements) {
  let dp = Array.from({ length: n + 1 }, () => new Array(401).fill(0));
  let req = new Array(n + 1).fill(-1);
  for (let [a, b] of requirements) {
    req[a + 1] = b;
  }

  if (req[1] <= 0) dp[1][0] = 1;

  for (let i = 2; i <= n; i++) {
    for (let c = 0; c < i; c++) {
      for (let j = 0; j + c <= 400; j++) {
        if (req[i] !== -1 && j + c !== req[i]) continue;
        dp[i][j + c] += dp[i - 1][j];
        dp[i][j + c] %= 1e9 + 7;
      }
    }
  }

  return dp[n][req[n]];
};

Go

func numberOfPermutations(n int, requirements [][]int) int {
	dp := make([][]int, n+1)
	for i := range dp {
		dp[i] = make([]int, 401)
	}

	req := make([]int, n+1)
	for i := range req {
		req[i] = -1
	}

	for _, r := range requirements {
		a := r[0]
		b := r[1]
		req[a+1] = b
	}

	const mod = 1e9 + 7

	if req[1] <= 0 {
		dp[1][0] = 1
	}

	for i := 2; i <= n; i++ {
		for c := 0; c < i; c++ {
			for j := 0; j+c <= 400; j++ {
				if req[i] != -1 && j+c != req[i] {
					continue
				}
				dp[i][j+c] += dp[i-1][j]
				dp[i][j+c] %= mod
			}
		}
	}

	return dp[n][req[n]]
}

C#

public class Solution {
    public int NumberOfPermutations(int n, int[][] requirements) {
        int[,] dp = new int[n + 1, 401];
        int[] req = new int[n + 1];
        Array.Fill(req, -1);

        foreach (var requirement in requirements) {
            int a = requirement[0];
            int b = requirement[1];
            req[a + 1] = b;
        }

        if (req[1] <= 0) {
            dp[1, 0] = 1;
        }

        for (int i = 2; i <= n; i++) {
            for (int c = 0; c < i; c++) {
                for (int j = 0; j + c <= 400; j++) {
                    if (req[i] != -1 && j + c != req[i]) continue;
                    dp[i, j + c] += dp[i - 1, j];
                    dp[i, j + c] %= 1000000007;
                }
            }
        }

        return dp[n, req[n]];
    }
}

Conclusion

In conclusion, the problem of counting permutations of an array that meet specific inversion requirements for certain segments can be effectively solved using dynamic programming. By leveraging a DP table to track the number of ways to arrange elements with a given number of inversions, and by carefully managing the inversion requirements for each segment, we can efficiently compute the desired count of valid permutations. The approach ensures that only valid configurations are considered, adhering to the constraints provided. The final result is obtained from the DP table, providing the number of permutations that satisfy the given conditions, modulo (10^9 + 7).

This solution offers a structured and scalable way to tackle permutation problems with constraints, making it a powerful technique for similar combinatorial challenges.