平衡树优化 DP: CF1416E Split

CF1416E Split

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

题目大意

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

DP 设计

设计 fij 表示枚举到 aib2i=j 的时候,合并后 b 的最小长度。下面是转移:

fi,j=min(fi1,aij+1,fi1,k+2)[2j=ai]

很显然这样是 O(nmaxa) 的,所以一定是过不掉的。

DP 优化

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

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

fi,j=min(fi1,aij,fi1,k+1)[2j=ai]

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

代码实现

我使用的平衡树是 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 @   Wild_Donkey  阅读(232)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2021-01-14 P3834 【模板】可持久化线段树 2(主席树)
2021-01-14 P3919 【模板】可持久化线段树 1(可持久化数组)
2021-01-14 P4168 [Violet]蒲公英
点击右上角即可分享
微信分享提示