平衡树优化 DP: CF1416E Split

CF1416E Split

这篇题解没有任何思维含量,属于数据结构强行优化 DP,比别的题解少用了一些性质。

题目大意

给一个长度为 \(n\) 的序列 \(a\),把每个数原地分裂成两个正数,变成长度为 \(2n\) 的序列 \(b\)。然后把 \(b\) 相邻的连续的相同数字合并成一个数字,求 \(b\) 合并后的最短长度。

DP 设计

设计 \(f_{i,j}\) 表示枚举到 \(a_i\)\(b_{2i} = j\) 的时候,合并后 \(b\) 的最小长度。下面是转移:

\[f_{i, j} = min(f_{i - 1, a_i - j} + 1, f_{i - 1, k} + 2) - [2j = a_i]\\ \]

很显然这样是 \(O(n\max a)\) 的,所以一定是过不掉的。

DP 优化

我们用平衡树维护一堆区间,表示 \(f\) 值,需要支持全局最值,后缀删除,后缀插入,全局抹平,全局翻转,全局加,单点减。(真是看看就不想写呢,不过还好大部分都是全局操作)

为了阉掉全局加操作,所以把状态设为最小长度减去 \(i\),这样转移就变成:

\[f_{i, j} = min(f_{i - 1, a_i - j}, f_{i - 1, k} + 1) - [2j = a_i]\\ \]

接下来只要写一棵趁手的平衡树维护一下就可以了,复杂度 \(O(n\log n)\)

代码实现

我使用的平衡树是 WBLT,如果不会 WBLT,强烈推荐学一下,可以维护区间,可以持久化,非均摊复杂度,而且常数小如 Treap,基本上可以代替 FHQ 的一切。

#define Inf 1000000
unsigned a[500005], m, n;
unsigned A, B, C, D, t;
unsigned Cnt(0), Ans(0), Tmp(0);
struct Node {
  Node* LS, * RS;
  unsigned Mn, L, R, Tag, Size;
  char Re;
  inline void Udt() { Size = LS->Size + RS->Size, Mn = min(LS->Mn, RS->Mn); }
}N[5000005], * CntN(N), * Root;
inline void Clr() { n = RD(), Root = N; }
inline void PsDw(Node* x) {
  if (x->LS) x->LS->R = x->L + x->LS->R - x->LS->L, x->LS->L = x->L;
  if (x->RS) x->RS->L = x->R - x->RS->R + x->RS->L, x->RS->R = x->R;
  if (x->Re) {
    swap(x->LS, x->RS);
    if ((x->LS) && (x->RS)) {
      x->LS->Re ^= 1, x->RS->Re ^= 1;
      x->LS->R = x->L + x->LS->R - x->LS->L;
      x->RS->L = x->LS->R + 1;
      x->LS->L = x->L, x->RS->R = x->R;
    }
    else {
      if (x->LS) x->LS->Re ^= 1;
      if (x->RS) x->RS->Re ^= 1;
    }
    x->Re = 0;
  }
  if (x->Tag) {
    if (x->LS) x->LS->Tag = x->LS->Tag ? min(x->LS->Tag, x->Tag) : x->Tag, x->LS->Mn = min(x->LS->Mn, x->Tag);
    if (x->RS) x->RS->Tag = x->RS->Tag ? min(x->RS->Tag, x->Tag) : x->Tag, x->RS->Mn = min(x->RS->Mn, x->Tag);
    x->Tag = 0;
  }
}
inline Node* Rotate(Node* x) {
  PsDw(x);
  if ((!(x->LS)) && (!(x->RS))) return x;
  if (!(x->RS)) return Rotate(x->LS);
  if (!(x->LS)) return Rotate(x->RS);
  if (x->Size < 5) return x;
  if ((x->LS->Size * 3) < x->RS->Size) {
    Node* Cur(Rotate(x->RS));
    x->RS = Cur->RS, Cur->RS = Cur->LS, Cur->LS = x->LS, x->LS = Cur, Cur->Udt();
    Cur->L = Cur->LS->L, Cur->R = Cur->RS->R;
  }
  if ((x->RS->Size * 3) < x->LS->Size) {
    Node* Cur(Rotate(x->LS));
    x->LS = Cur->LS, Cur->LS = Cur->RS, Cur->RS = x->RS, x->RS = Cur, Cur->Udt();
    Cur->L = Cur->LS->L, Cur->R = Cur->RS->R;
  }
  return x;
}
inline Node* Edit(Node* x) {
  Rotate(x);
  if ((!(x->LS)) && (!(x->RS))) {
    if (x->L == x->R) --(x->Mn);
    else {
      if (x->L == A) {
        x->LS = (++CntN), * (x->LS) = (Node){ NULL, NULL, x->Mn - 1, A, A, 0, 1, 0 };
        x->RS = (++CntN), * (x->RS) = (Node){ NULL, NULL, x->Mn, A + 1, x->R, 0, 1, 0 };
        x->Udt();
        return x;
      }
      if (x->R == A) {
        x->RS = (++CntN), * (x->RS) = (Node){ NULL, NULL, x->Mn - 1, A, A, 0, 1, 0 };
        x->LS = (++CntN), * (x->LS) = (Node){ NULL, NULL, x->Mn, x->L, A - 1, 0, 1, 0 };
        x->Udt();
        return x;
      }
      x->LS = (++CntN), * (x->LS) = (Node){ NULL, NULL, x->Mn, x->L, A, 0, 1, 0 };
      x->RS = (++CntN), * (x->RS) = (Node){ NULL, NULL, x->Mn, A + 1, x->R, 0, 1, 0 };
      x->LS = Edit(x->LS), x->Udt();
    }
  }
  else {
    if (x->LS->R >= A) x->LS = Edit(x->LS);
    else x->RS = Edit(x->RS);
    x->Udt();
  }
  return x;
}
inline Node* Del(Node* x) {
  if (x->L > A) return NULL;
  x = Rotate(x);
  if (!(x->LS)) { x->R = A;return x; }
  if (x->LS->R > A) return Del(x->LS);
  x->RS = Del(x->RS), x->R = A, x = Rotate(x);
  if (x->LS) x->Udt();
  return x;
}
inline Node* Isrt(Node* x) {
  x = Rotate(x);
  if (x->RS) { x->RS = Isrt(x->RS), x->R = A, x->Udt(); return Rotate(x); }
  if (x->Mn == B) { x->R = A; return x; }
  x->LS = (++CntN), * (x->LS) = (Node){ NULL, NULL, x->Mn, x->L, x->R, 0, 1, 0 };
  x->RS = (++CntN), * (x->RS) = (Node){ NULL, NULL, B, x->R + 1, A, 0, 1, 0 };
  x->R = A, x->Udt();  return Rotate(x);
}
signed main() {
  t = RD();
  for (unsigned T(1); T <= t; ++T) {
    Clr();
    for (unsigned i(1); i <= n; ++i) a[i] = RD();
    N[0] = (Node){ NULL, NULL, Inf + 1, 1, a[1] - 1, 0, 1, 0 };
    if (!(a[1] & 1)) A = a[1] >> 1, Root = Edit(Root);
    for (unsigned i(2); i <= n; ++i) {
      B = Root->Mn + 1;
      A = a[i] - 1;
      if (a[i] > a[i - 1]) Root = Isrt(Root);
      if (a[i] < a[i - 1]) Root = Del(Root);
      Root = Rotate(Root), Root->Tag = B, Root->Re = 1, Root->Mn = min(B, Root->Mn);
      if (!(a[i] & 1)) A = a[i] >> 1, Root = Edit(Root);
    }
    printf("%u\n", Root->Mn + n - Inf);
  }
  return Wild_Donkey;
}
posted @ 2022-01-14 11:17  Wild_Donkey  阅读(170)  评论(1编辑  收藏  举报