AOJ - MORSE

같은 것이 있는 순열의 개수는 $\dfrac {(n+m)!}{n! \cdot m!}=\dbinom {n+m}n$ 이다. 이를 이용해서 $O(N^2)$에 일단 이항 계수 테이블을 만들어준다.

이후에 $K$ 번째 수를 찾는것은 $K-1$ 개의 수를 $skip$ 해야 한다는 것에 기반한다.

먼저 $K$ 를 1을 빼준다음에 그만큼 스킵을 시켜주는 것이다.

-를 앞에 붙일 수 있다는것은 -의 개수를 하나 빼고 만드는 조합의 개수가 $K$ 개보다 많아서 -를 붙이고도 진행을 할 수 있음을 의미한다.

현재 -와 o가 $x$개 $y$개로 모두 남아있을 때, -를 써서 $x-1$ 개의 -와 $y$ 개의 o를 써서 만들 수 있는 순열의 개수는 $\dbinom {x-1+y}y$ 개이고 이것이 현재 스킵해야될 개수인 $K$보다 많다면 그냥 -를 붙여준다.

그렇지 않다면 o를 붙여주고 -를 붙였을 때의 경우의 수인 저만큼 $K$에서 빼주면 된다.

int bino[222][222];  
void solve() {  
   int n, m, k;  
   cin >> n >> m >> k;  
   // 같은것이 있는 순열  
   // (n+m)! / (n! * m!)  
   for (int i = 0; i <= 222; i++)  
      for (int j = 0; j <= i; j++)  
         bino[i][j] = !i || !j ? 1 : min(bino[i - 1][j] + bino[i - 1][j - 1], int(1e9));  
  
   string ans;  
   function<void(int, int)> fn = [&](int i, int j) -> void {  
      if (i == 0 && j == 0) return;  
      if (!i) {  
         ans += 'o';  
         fn(i, j - 1);  
         return;  
      }  
      if (!j) {  
         ans += '-';  
         fn(i - 1, j);  
         return;  
      }  
  
      // i 를 썻을 때  
      int x = bino[i - 1 + j][j];  
      if (x > k) {  
         ans += '-';  
         fn(i - 1, j);  
      } else {  
         k -= x;  
         ans += 'o';  
         fn(i, j - 1);  
  
      }  
  
   };  
   k--;  
   fn(n, m);  
   cout << ans << endl;  
}

Comments