BOJ 9623 - 부분 수열의 길이

image.png

결국 어떠한 연속된 구간의 합은 $S[i]=\sum_{k=0}^i A[k]$ 라 할 때,

$S[i]-S[j]~(j < i)$ 이다.

$S[i]$ 를 계속 가지고가면, $S[i]-S[j] \ge X$를 만족하려면 $S[i]-X \ge S[j]$ 여야 한다.

그럼 현재까지 본 $S[j]$ 중, $S[i]-X$ 이하인 $j$ 중 가장 큰 $j$를 구하는 문제가 된다.

세그먼트 트리 풀이가 생각나서 바로 풀었는데, $S[j]$ 를 인덱스로 가지고 값을 $j$ 로 가지게끔 해서 $-\infty \sim S[i]-X$ 까지 범위에서 가장 큰 인덱스를 찾아오면 된다.

이렇게 쿼리하고 정답을 갱신해준 뒤에 $(S[i], i)$ 를 트리에 업데이트해준다.

세그 트리를 쓰면 좌표압축을 하거나 다이나믹 세그먼트 트리를 써야해서 구현이 까다로울 수 있다.

const int inf = 2e16;  
// Maximum Value Dynamic Segment Tree  
struct node {  
   int v = -inf, l = -1, r = -1;  
};  
  
struct dynamic_seg_tree {  
   int N, nxt = 1;  
   vector<node> tree;  
   dynamic_seg_tree(int N) : N(N) {  
      tree.pb({});  
   }  
  
   void update(int n, int nl, int nr, int i, int v) {  
      if (nr < i || nl > i) return;  
      if (nl == nr) {  
         tree[n].v = v;  
         return;  
      }  
      int m = (nl + nr) / 2;  
      if (i <= m) {  
         if (tree[n].l == -1) {  
            tree[n].l = nxt++;  
            tree.pb({});  
         }  
         update(tree[n].l, nl, m, i, v);  
      } else {  
         if (tree[n].r == -1) {  
            tree[n].r = nxt++;  
            tree.pb({});  
         }  
         update(tree[n].r, m + 1, nr, i, v);  
      }  
      tree[n].v = max(~tree[n].l ? tree[tree[n].l].v : -inf, ~tree[n].r ? tree[tree[n].r].v : -inf);  
   }  
   void update(int i, int v) {  
      update(0, 0, N - 1, i, v);  
   }  
  
   int query(int n, int nl, int nr, int l, int r) {  
      if (n == -1 || nr < l || nl > r) return -inf;  
      if (nl >= l && nr <= r) return tree[n].v;  
      int m = (nl + nr) / 2;  
      return max(query(tree[n].l, nl, m, l, r), query(tree[n].r, m + 1, nr, l, r));  
   }  
   int query(int l, int r) {  
      return query(0, 0, N - 1, l, r);  
   }  
};  
  
void solve() {  
   int n, x;  
   cin >> n >> x;  
   vi a(n);  
   fv(a);  
   dynamic_seg_tree seg(inf);  
   int sum = 0;  
   seg.update(inf / 2, 0);  
   int ans = inf;  
   for (int i = 0; i < n; i++) {  
      sum += a[i];  
      int ret = seg.query(0, sum - x + inf / 2);  
      if (ret != -inf) ans = min(ans, i - ret + 1);  
      seg.update(sum + inf / 2, i + 1);  
   }  
   cout << (ans == inf ? -1 : ans) << endl;  
}

Tags:

Categories:

Updated:

Comments