Problem Solving

BOJ 19651. 수열과 쿼리 39

thisisuserr 2024. 9. 4. 21:06

19651번: 수열과 쿼리 39 (acmicpc.net)

solved.ac: Diamond V (2024.09.06)

 

나이브하게 처리하는 것은 상당히 노답이므로, 문제를 변형시킬 필요가 있습니다.

등차수열에서 주로 사용되는 성질로 A[i-1] + A[i+1] = 2A[i]를 이용합시다.

 

새로운 배열을 생각해서 B[i] = A[i-1] + A[i+1] - 2A[i]이라 하면,

세 수 A[i-1], A[i], A[i+1]가 등차수열을 이룰 시 B[i] = 0이 됩니다.

따라서 쿼리의 답은 (연속해서 0인 B[i]의 개수) + 2가 됩니다. 이는 금광세그 트릭을 이용하면 구할 수 있습니다.

 

업데이트의 경우 세 수가 모두 포함되거나 포함되지 않을 경우 변화량은 없으므로,

쿼리 [s, e]에 대해 B[s - 1], B[s], B[e], B[e + 1]만 업데이트됩니다.

 

자세한 풀이는 코드를 참고해 주세요.

#include <iostream>
#include <vector>

using namespace std;
typedef long long int LL;

/*
B[i] = A[i-1] + A[i+1] - 2A[i]로 정의하면, (연속한 0의 개수 + 2)를 구하는 문제로 바뀐다.
구간 [s, e]에 (x + nd)를 더한다고 해 보자.

B[s - 1] = A[s - 2] + A[s] - 2A[s - 1]에서, 변화량은 x이다.
B[s] = A[s - 1] + A[s + 1] - 2A[s]에서, 변화량은 d - x이다.

B[e] = A[e - 1] + A[e + 1] - 2A[e]에서, 변화량은 (s - e - 1)d - x이다.
B[e + 1] = A[e] + A[e + 2] - 2A[e + 1]에서, 변화량은 (e - s)d + x이다.
*/

/* 연속한 0의 개수 관리: 금광 세그 */
struct seg
{
bool all;
int lmax, rmax, mx, sz;
};

LL arr[400001];
seg tree[400001];

seg merge(seg &l, seg &r)
{
  seg res;
  res.all = l.all && r.all;
  res.lmax = (l.all ? l.sz + r.lmax : l.lmax);
  res.rmax = (r.all ? l.rmax + r.sz : r.rmax);
  res.mx = max(max(l.mx, r.mx), l.rmax + r.lmax);
  res.sz = l.sz + r.sz;
  return res;
}

void update(int node, int s, int e, int idx, int val)
{
  if(idx < s || e < idx) return;
  if(s == e) // update
  {
    arr[node] += val;
    tree[node].sz = 1;
    
    if(arr[node] == 0)
    {
      tree[node].lmax = 1; tree[node].rmax = 1; 
      tree[node].mx = 1;   tree[node].all = true;
    }
    else
    {
      tree[node].lmax = 0; tree[node].rmax = 0;
      tree[node].mx = 0;   tree[node].all = false;
    }
    return;
  }

  int m = (s + e) / 2;
  update(node * 2, s, m, idx, val);
  update(node * 2 + 1, m + 1, e, idx, val);

  tree[node] = merge(tree[node * 2], tree[node * 2 + 1]);
}

seg query(int node, int s, int e, int l, int r)
{
  if(r < s || e < l) return {true, 0, 0, 0, 0};
  if(l <= s && e <= r) return tree[node];

  int m = (s + e) / 2;
  seg left = query(node * 2, s, m, l, r);
  seg right = query(node * 2 + 1, m + 1, e, l, r);
  return merge(left, right);
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int n;
  cin >> n;

  vector<int> init(n + 1);
  for(int i = 1; i <= n; i++) cin >> init[i];
  for(int i = 2; i < n; i++) update(1, 1, n, i, init[i - 1] + init[i + 1] - 2 * init[i]);

  int Q;
  cin >> Q;
  while(Q--)
    {
      int op;
      cin >> op;

      if(op == 1)
      {
        int s, e, x, d;
        cin >> s >> e >> x >> d;

        update(1, 1, n, s - 1, x);
        update(1, 1, n, s, d - x);
        update(1, 1, n, e, (s - e - 1) * d - x);
        update(1, 1, n, e + 1, (e - s) * d + x);
      }
      else
      {
        int l, r;
        cin >> l >> r;
        cout << query(1, 1, n, l + 1, r - 1).mx + 2 << '\n';
      }
    }
}

'Problem Solving' 카테고리의 다른 글

BOJ 11011. Forged Answer  (0) 2024.09.13
BOJ 13576. Prefix와 Suffix  (0) 2024.09.10
BOJ 12963. 달리기  (0) 2024.09.03
BOJ 17501. 수식 트리  (0) 2024.08.30
BOJ 11378. 열혈강호 4  (0) 2024.08.27