AOJ - KLIS

아마 종만북으로 처음 알고리즘을 접한다면 이 때쯤부터 통곡의 벽이 올 수 있다.

일단 책에있는 설명과 다르지 않게 풀었고,

뒤에서부터 LDS 를 구하면 그것이 특정 숫자에서 시작할 수 있는 LIS 의 최대 길이가 나오므로, $O(Nl\log N)$에 구해준다.

이후 $i$ 에서 시작할 수 있는 최대 길이의 LIS의 개수들을 $O(N^2)$ 에 돌려준다.

이후 $O(N^2\log N)$ 으로 trace를 하는데, 책에선 이걸 $O(N^2)$에 찾을 수 있다고 했는데,

아마 이전 과정에서 전처리를 해두라는 것을 의미하는 것 같다.

void solve() {  
   int n, k;  
   cin >> n >> k;  
  
   vi a(n);  
   fv(a);  
   n++;  
   a.insert(a.begin(), -1e15);  
  
   vi m, lis_len(n);  
   for (int i = n - 1; i >= 0; i--) {  
      int j = lbi(m, -a[i]);  
      if (j == sz(m)) m.pb(-a[i]);  
      else m[j] = -a[i];  
      lis_len[i] = j + 1;  
   }  
   int LIS = sz(m);  
  
   vi dp(n + 1, -1);  
   function<int(int)> fn = [&](int i) -> int {  
      if (lis_len[i] == 1) return 1;  
      int &ret = dp[i];  
      if (~ret) return ret;  
      ret = 0;  
      for (int j = i + 1; j < n; j++) {  
         if (a[i] < a[j] && lis_len[i] - 1 == lis_len[j]) {  
            ret = min(ret + fn(j), int(1e12));  
         }  
      }  
      return ret;  
   };  
  
   k--;  
  
   vi ans;  
   function<void(int)> trace = [&](int i) -> void {  
      ans.pb(a[i]);  
      if (lis_len[i] == 1) {  
         return;  
      }  
      vector<pi> nxt_list;  
      for (int j = i + 1; j < n; j++) {  
         if (a[i] < a[j] && lis_len[i] - 1 == lis_len[j]) {  
            nxt_list.pb({a[j], j});  
         }  
      }  
      sort(all(nxt_list));  
      for (int j = 0; j < sz(nxt_list); j++) {  
         int M = fn(nxt_list[j].se);  
         if (M > k) {  
            trace(nxt_list[j].se);  
            return;  
         } else {  
            k -= M;  
         }  
      }  
   };  
   trace(0);  
  
   cout << sz(ans) - 1 << endl;  
   for (int i: ans) if (i != -1e15) cout << i << ' ';  
   cout << endl;  
}

Comments