其他分享
首页 > 其他分享> > 月赛 101 Div1D 分块神仙题

月赛 101 Div1D 分块神仙题

作者:互联网

分块 RMSQ

传送门

题面已经很清楚了,所以就不复述了。

转化

我们发现排列不需要存储,只要存储每个元素的位置 \(Pos_i\) 即可,读入 \(a\) 的时候,我们把输入的第 \(i\) 数字在 \(b\) 中的位置 \(Pos_i\) 存入 \(a_i\)。这样问题就变成了在新的 \(a\) 数组中查询区间的最长连续上升子序列。

离线

对于离线算法,我们想到莫队。通过存储当前区间内,以 \(a\) 的每个位置结尾和开头的子序列长度 \(Len_{i, 0/1}\),可以进行 \(O(1)\) 的边界伸长。利用回滚莫队,设置块长为 \(\dfrac n{\sqrt q}\) 可以做到 \(O(m + n\sqrt q)\) 的总复杂度。

在线

同样是分块,设块长为 \(B\),块数记为 \(A = \lfloor \frac nB \rfloor\)。

这个算法和出题人的官方算法不同,我预处理了 \(f\),\(g\),\(Mx\) 三个数组。

\(f_{i, j, k}\) 表示从第 \(i\) 个到第 \(j\) 个整块,外加右边紧挨着的 \(k\) 个零散元素组成的区间 \([iB + 1, (j + 1)B + k]\) 内,以 \(k\) 为结尾的最长的满足条件的子序列长度。

\(g_{i, j, k}\) 表示从第 \(i\) 个到第 \(j\) 个整块,外加左边紧挨着的 \(k\) 个零散元素组成的区间 \([iB + 1 - k, (j + 1)B]\) 内,以 \(k\) 为开头的最长的满足条件的子序列长度。

\(Mx_{i, j}\) 表示从第 \(i\) 个到第 \(j\) 个整块组成的区间 \([i * B + 1, (j + 1) * B]\) 内最长满足条件的子序列的长度。

预处理的过程和莫队区间伸长是一样的。复杂度 \(O(\frac {n^2}B)\)。

询问的时候把查询的子序列分为 \(3\) 种:

这样可以在 \(O(B)\) 内完成一次查询。块长取 \(\dfrac n{\sqrt q}\) 的时候可以得到 \(O(m + n\sqrt q)\) 的复杂度。

代码实现

这里我把 \(n\),\(m\) 的意义反过来了,这里提醒一下避免误会。

对于 \(f\) 和 \(g\),由于空间不够,所以采用了公用空间的手段。发现所有 \(i > j\) 的 \(f_{i, j, k}\) 和 \(g_{i, j, k}\) 都不存在。对于 \(f\) 来说,\(i\) 的取值为 \([0, A)\)。而 \(g\) 的 \(i\) 取值为 \((1, A)\)。所以我们可以把 \(g_{i, j, k}\) 的值存到 \(f_{A - i, A - j, k}\) 中去。为了防止 \(i = j\) 的时候,\(f\) 和 \(g\) 冲突的情况,我们把原本 \(f_{i, j, k}\) 的值存到 \(f_{i, j + 1, k}\) 中去。这样第 \(i\) 行,\(f\) 占用第 \((i, j]\) 列,\(g\) 占用第 \((0, i]\) 列。不产生冲突。

但是随着块长的变化,定义全局变量三维数组的行为无法满足灵活变化的块长的需求,频繁的访问寻址也会增加常数,所以我们只开一个内存池,然后灵活地查询我们想要的位置的指针,在连续访问的时候只移动指针而避免了三个下标的寻址。

unsigned fPool[300000000], Len[300005], Mx[1005][1005];
unsigned a[300005], Pos[300005], m, n, q;
unsigned Stack[2005], STop(0);
unsigned A, B, C, D, TmpM;
unsigned PrA, PrB;
unsigned Cnt(0), Ans(0), Tmp(0);
char Use(0);
inline unsigned* f(const unsigned x, const unsigned y, const unsigned z) {
  return fPool + x * PrA + y * PrB + z;
}
signed main() {
  n = RD(), m = RD(), q = RD(), Use = RD(), B = min(m, (unsigned)max((double)m * m / 300001000, (m / sqrt(q))) + 1), A = m / B;
  PrB = B + 1, PrA = PrB * (A + 1);
  for (unsigned i(1); i <= n; ++i) Pos[RD()] = i;
  for (unsigned i(1); i <= m; ++i) a[i] = Pos[RD()];
  for (unsigned i(0); i < A; ++i) {
    memset(Len, 0, ((n + 1) << 2)), TmpM = 0;
    for (unsigned j(i), k = (i * B + 1); j < A; ++j) {
      unsigned* CuPo(f(i, j, 1));
      for (unsigned Cou(1); Cou <= B; ++Cou, ++k, ++CuPo) {
        unsigned Cur(a[k]), Pre(Len[Cur - 1] + 1);
        Len[Cur] = max(Pre, Len[Cur]), TmpM = max(TmpM, Len[Cur]);
        if (j > i) (*CuPo) = Len[Cur];
      }
      Mx[i][j] = TmpM;
    }
    unsigned* CuPo(f(i, A, 1));
    for (unsigned k(A* B + 1); k <= m; ++k, ++CuPo) {
      unsigned Cur(a[k]), Pre(Len[Cur - 1] + 1);
      Len[Cur] = max(Pre, Len[Cur]), (*CuPo) = Len[Cur];
    }
  }
  for (unsigned i(A - 1); i; --i) {
    memset(Len, 0, ((n + 1) << 2));
    for (unsigned j(i), k = ((i + 1) * B); ~j; --j) {
      unsigned* CuPo(f(A - j - 1, A - i, 1));
      for (unsigned Cou(1); Cou <= B; ++Cou, --k, ++CuPo) {
        unsigned Cur(a[k]), Pre(Len[Cur + 1] + 1);
        Len[Cur] = max(Pre, Len[Cur]);
        if (j < i) (*CuPo) = Len[Cur];
      }
    }
  }
  memset(Len, 0, ((n + 1) << 2));
  for (unsigned i(1); i <= q; ++i) {
    if (!Use) Ans = 0;
    C = (RD() ^ Ans), D = (RD() ^ Ans), Ans = 0;
    unsigned L((C + B - 2) / B), R(D / B);
    if (L >= R) {
      for (unsigned i(C); i <= D; ++i) {
        unsigned Cur(a[i]), Pre(Len[Cur - 1] + 1);
        Stack[++STop] = Cur, Ans = max(Ans, Len[Cur] = max(Len[Cur], Pre));
      }
      while (STop) Len[Stack[STop--]] = 0;
      printf("%u\n", Ans); continue;
    }
    --R, Ans = max(Ans, Mx[L][R]), C = (L * B) - C + 1, D -= (R + 1) * B;
    unsigned* CuPo(f(L, R + 1, 1));
    for (unsigned j(1), p(((R + 1)* B) + 1); j <= D; ++j, ++p, ++CuPo) {
      unsigned TheL(*CuPo), Le(a[p] - TheL + 1);
      Ans = max(Ans, TheL);
      Stack[++STop] = Le, Len[Le] = max(Len[Le], TheL);
    }
    for (unsigned j(1), p(L* B); j <= C; ++j, --p) {
      unsigned Cur(a[p]), Pre(Len[Cur + 1] + 1);
      Stack[++STop] = Cur, Ans = max(Ans, Len[Cur] = max(Len[Cur], Pre));
    }
    while (STop) Len[Stack[STop--]] = 0;
    CuPo = f(A - L, A - R, 1);
    for (unsigned j(1); j <= C; ++j, ++CuPo) Ans = max(Ans, *CuPo);
    printf("%u\n", Ans);
  }
  return Wild_Donkey;
}

标签:月赛,零散,元素,unsigned,Len,Div1D,区间,序列,101
来源: https://www.cnblogs.com/Wild-Donkey/p/15876840.html