首页 > 其他分享 >P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II 题解

P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II 题解

时间:2023-09-29 21:55:37浏览次数:41  
标签:Ynoi2019 kMaxB suf int 题解 个块 kMaxN P5047 逆序

Description

给你一个长为 \(n\) 的排列,\(m\) 次询问,每次查询一个区间的逆序对数,强制在线。

link

\(1\leq n,m\leq 10^5\)。

Solution

考虑分块。

首先如果 \(l,r\) 在同一个块内,可以对于每个块暴力二维前缀和预处理。

如果 \(l,r\) 在不同的块内。

设 \(bel[l]=x,bel[r]=y\)。

首先考虑 \(x+1\sim y-1\) 块内的贡献,这个显然可以预处理。

然后是 \(l\sim R_x\) 和 \(L_y\sim r\) 的块内贡献,这个可以预处理 \(A_i\) 表示 \(i\) 到 \(i\) 所在块的末尾这些数的逆序对个数,\(B_i\) 表示 \(i\) 到这个块开头的逆序对数。

这两个还是可以预处理。

然后就是 \(x+1\sim y-1\) 块之间的贡献,维护 \(f_{i,j}\) 表示前 \(i\) 个块到和前 \(j\) 个块的逆序对数。

这个显然可以先枚举 \(i\) 块,维护一个值域数组,然后暴力枚举其他的块统计答案,最后做个二维前缀和即可。

注意到这个玩意是没有顺序的,所以统计答案时要除以 \(2\)。


随后是 \([l,R_x],[L_y,r]\) 到 \([x+1,y-1]\) 块的贡献。

这个可以预处理 \(g_{i,j}\) 表示前 \(i\) 个块到和前 \(j\) 个的逆序对数,同样可以二维前缀和搞。

容易发现这个东西是不要除以 \(2\) 的。


最后是 \([l,R_x]\) 和 \([L_y,r]\) 之间的贡献。

这个东西考虑把 \([l,R_x]\) 和 \([L_y,r]\) 都 sort 一遍然后跑双指针。

但这样就带 \(\log\) 了。

解决方案是初始时对于每个块维护一个 sort 后的 pair 数组,第一维是权值,第二维是下标。

然后从前到后枚举 \(x\) 和 \(y\) 块的 pair 数组,如果下标在 \([l,r]\) 里就把权值加到新数组里即可。

时间复杂度:\(O((n+q)\sqrt n)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

using i64 = int64_t;

const int kMaxN = 1e5 + 5, kMaxB = 320;

int n, m, bl, tot;
int a[kMaxN], bel[kMaxN], L[kMaxB], R[kMaxB], sz[kMaxB], suf[kMaxN], cnt5[kMaxB][kMaxB][kMaxB];
std::pair<int, int> b[kMaxN];
i64 cnt1[kMaxB], cnt2[kMaxB][kMaxB], cnt3[kMaxN], cnt4[kMaxN], cnt6[kMaxB][kMaxN];

/*
  cnt1[i] : 前 i 个块内部的逆序对数量和
  cnt2[i][j] : 前 i 个块跟前 j 个块的逆序对数量和
  cnt3[i] : i 到 i 所在块结尾这些数的逆序对数量
  cnt4[i] : i 到 i 所在块开头这些数的逆序对数量
  cnt5[i][j][k] : 第 i 个块前 j 个数到前 k 个数的逆序对数量
  cnt6[i][j] : 前 i 个块跟前 j 个数的逆序对数量和
*/

struct BIT {
  int c[kMaxN];

  void upd(int x, int v) {
    for (; x <= n; x += x & -x)
      c[x] += v;
  }

  int qry(int x) {
    int ret = 0;
    for (; x; x -= x & -x)
      ret += c[x];
    return ret;
  }
  int qry(int l, int r) { return qry(r) - qry(l - 1); }
} bit;

i64 brute_force(int *a, int l1, int r1, int *b, int l2, int r2) {
  i64 ret = 0;
  int p = l2 - 1;
  for (int i = l1; i <= r1; ++i) {
    for (; p < r2 && b[p + 1] < a[i]; ++p) {}
    ret += p - l2 + 1;
  }
  return ret;
}

void prework() {
  bl = sqrt(n), tot = (n - 1) / bl + 1;
  for (int i = 1; i <= n; ++i)
    b[i] = {a[i], i};
  for (int i = 1; i <= tot; ++i) {
    L[i] = R[i - 1] + 1, R[i] = std::min(i * bl, n);
    std::sort(b + L[i], b + R[i] + 1);
    for (int j = L[i]; j <= R[i]; ++j)
      bel[j] = i;
  }
  for (int i = 1; i <= tot; ++i) {
    cnt1[i] = cnt1[i - 1];
    for (int j = L[i]; j <= R[i]; ++j)
      for (int k = j + 1; k <= R[i]; ++k)
        if (a[j] > a[k])
          ++cnt1[i], ++cnt5[i][j - L[i] + 1][k - L[i] + 1];
    for (int j = 1; j <= bl; ++j)
      for (int k = 1; k <= bl; ++k)
        cnt5[i][j][k] += cnt5[i][j - 1][k] + cnt5[i][j][k - 1] - cnt5[i][j - 1][k - 1];

    for (int j = R[i]; j >= L[i]; --j) {
      if (j < R[i]) cnt3[j] = cnt3[j + 1] + bit.qry(a[j] - 1);
      bit.upd(a[j], 1);
    }
    for (int j = L[i]; j <= R[i]; ++j)
      bit.upd(a[j], -1);
    for (int j = L[i]; j <= R[i]; ++j) {
      if (j > L[i]) cnt4[j] = cnt4[j - 1] + bit.qry(a[j] + 1, n);
      bit.upd(a[j], 1);
    }
    for (int j = L[i]; j <= R[i]; ++j)
      bit.upd(a[j], -1);
  
    for (int j = L[i]; j <= R[i]; ++j)
      ++suf[a[j]];
    for (int j = n; j; --j)
      suf[j] += suf[j + 1];
    for (int j = 1; j <= n; ++j) {
      if (bel[j] > i) cnt2[i][bel[j]] += suf[a[j] + 1], cnt6[i][j] += suf[a[j] + 1];
      else if (bel[j] < i) cnt2[i][bel[j]] += suf[1] - suf[a[j]], cnt6[i][j] += suf[1] - suf[a[j]];
    }
    std::fill_n(suf + 1, n, 0);
  }
  for (int i = 1; i <= tot; ++i)
    for (int j = 1; j <= tot; ++j)
      cnt2[i][j] += cnt2[i - 1][j] + cnt2[i][j - 1] - cnt2[i - 1][j - 1];
  for (int i = 1; i <= tot; ++i)
    for (int j = 1; j <= n; ++j)
      cnt6[i][j] += cnt6[i - 1][j] + cnt6[i][j - 1] - cnt6[i - 1][j - 1];
}

i64 query(int l, int r, int cs) {
  static int tmpa[kMaxN], tmpb[kMaxN];
  int x = bel[l], y = bel[r];
  if (x == y) {
    int pl = l - L[x] + 1, pr = r - L[x] + 1;
    return cnt5[x][pr][pr] - cnt5[x][pl - 1][pr] - cnt5[x][pr][pl - 1] + cnt5[x][pl - 1][pl - 1];
  } else {
    i64 ret = 0;
    ret += cnt3[l] + cnt4[r] + cnt1[y - 1] - cnt1[x];
    ret += ((cnt2[y - 1][y - 1] - cnt2[x][y - 1]) - (cnt2[y - 1][x] - cnt2[x][x])) / 2;
    ret += (cnt6[y - 1][r] - cnt6[x][r]) - (cnt6[y - 1][L[y] - 1] - cnt6[x][L[y] - 1]);
    ret += (cnt6[y - 1][R[x]] - cnt6[x][R[x]]) - (cnt6[y - 1][l - 1] - cnt6[x][l - 1]);
    int ca = 0, cb = 0;
    for (int i = L[x]; i <= R[x]; ++i)
      if (b[i].second >= l && b[i].second <= R[x])
        tmpa[++ca] = b[i].first;
    for (int i = L[y]; i <= R[y]; ++i)
      if (b[i].second >= L[y] && b[i].second <= r)
        tmpb[++cb] = b[i].first;
    ret += brute_force(tmpa, 1, ca, tmpb, 1, cb);
    return ret;
  }
}

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = 1; i <= n; ++i)
    std::cin >> a[i];
  prework();
  i64 lastans = 0;
  for (int i = 1; i <= m; ++i) {
    i64 l, r;
    std::cin >> l >> r;
    l ^= lastans, r ^= lastans;
    std::cout << (lastans = query(l, r, i)) << '\n';
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:Ynoi2019,kMaxB,suf,int,题解,个块,kMaxN,P5047,逆序
From: https://www.cnblogs.com/Scarab/p/17737415.html

相关文章

  • CSP-S 2021 廊桥分配 题解
    part1:题目描述:当一架飞机抵达机场时,可以停靠在航站楼旁的廊桥,也可以停靠在位于机场边缘的远机位。乘客一般更期待停靠在廊桥,因为这样省去了坐摆渡车前往航站楼的周折。然而,因为廊桥的数量有限,所以这样的愿望不总是能实现。机场分为国内区和国际区,国内航班飞机只能停靠在国内......
  • [CF762D] Maximum path 题解
    [CF762D]Maximumpath题解想法首先考虑问题的弱化版,如果不能往左走,能取到的最大值是多少。这个问题可以用一个显然的DP解决,\(f_{i,j}\)表示走到第\(i\)列,第\(j\)行,并且不会再访问这一列其它的方格,能取到的最大值。转移可以从三个方向考虑,以\(f_{i,1}\)为例,黑色是当......
  • P2216 [HAOI2007] 理想的正方形 题解
    Description给定\(n\timesm\)的矩阵,找大小为\(k\timesk\)的子矩阵\(a\),使得子矩阵\(\max\{a\}-\min\{a\}\)最小。SolutionSolution1枚举所有\(k\timesk\)的子矩阵,然后枚举最大值和最小值,时间复杂度\(O(n^4)\),期望得分\(20\)分。Solution2求最大值和最小......
  • CF1425F Flamingoes of Mystery 题解
    题目传送门前置知识前缀和&差分解法令\(sum_k=\sum\limits_{i=1}^{k}a_k\)。考虑分别输入\(sum_2\simsum_n\),故可以由于差分知识得到\(a_i=sum_i-sum_{i-1}(3\lei\len)\),接着输入\(a_2+a_3\)的值从而求出\(a_2=sum_3-a_3,a_1=sum_2-a_2\)。同时因为是交互题,记......
  • 【题解】[CQOI2008] 传感器网络
    题意给定一张有向无环图,从中选出一棵有根树(节点编号为\(0\simn\),树根为\(n\)),使得除根节点外所有节点的出度的最大值最小。除根节点外,依次输出每个节点的父亲,并要求字典序最小。(\(1\len\le50\))*注意:由于个人习惯,这里将节点编号重编为\(1\simn+1\),根节点即为\(n+1\)......
  • 雅思 outweigh 议论题解答方法
    Doyouthinktheadvantagesoutweighthedisadvantages?1.这是两面写作题2.必须分出多少/高低3.所以说这个题目基本等同于discuss的弱强写作结构,不要给自己增加负担觉得新学了一种题目。结构安排:1.引出争论/背景句+给出明确的偏向(…..doesmoregoodthan harm.)2.先......
  • Broken robot 题解
    题目链接Rrokenrobot分析记\(f[i][j]\)为从\(i\)行\(j\)列到最后一行的期望,则\(f[i][j]=\begin{cases}\frac{1}{3}(f[i][j]+f[i][j+1]+f[i+1][j])+1&i=1\\\frac{1}{4}(f[i][j]+f[i][j-1]+f[i,j+1]+f[i+1][j])+1&1<i<m\\\frac{1}{3}(f[i][j]......
  • git clone项目报错fatal: fetch-pack: invalid index-pack output问题解决
    gitclone项目报错fatal:fetch-pack:invalidindex-packoutput问题解决原因出现该问题的原因是gitclone的项目过大导致项目拉去失败解决方法首先拉去项目最后一次提交gitclone--depth=1项目地址;拉取全部项目内容gitfetch--unshallow,一般不大的项目都可以......
  • ABC321题解
    以后应该都是从E开始。E:problemLCA题。我们枚举向上跳\(t\)步,跳到了\(y\)。假如说\(t=0\)那么我们计算\(\text{clac}(x,k)\)即可。(\(\text{clac}\)怎么算放在最后讲)否则计算\(\text{clac}(y,k)-\text{clac}(x>>(t-1),m-t-1)\)。(建议自己理解一下......
  • P2427 题解
    洛谷链接题目简述给定\(N\timesM\)的字符矩阵,有\(Q\)次询问,对于每次询问给出\(x,y\),求以\((x,y)\)为中心的最大正方形边长且正方形中字符均相同。思路看到数据范围较小,可以考虑深搜解决,约掉常数的时间复杂度最坏为\(O(q\times\min(n,m))\),勉强可以通过。(不过代码......