P3157 [CQOI2011]动态逆序对

知识点: CDQ 分治

原题面 Luogu


题意简述

给定一个 \(1\sim n\) 的排列,按照给定次序删除 \(m\) 个元素。
求在每次删除一个元素之前,整个序列的逆序对数。
\(1\le n\le 10^5\)\(1\le m\le 50000\)


分析题意

考虑离线,并反转操作次序。
问题变为给定一个序列 \(a\),每次添加一个元素 \(a_i\),求序列的逆序对数。

对于 \(a_i\),设其被添加的顺序为 \(t_i\)
设当前添加到 \(t_i\) 个元素。
对于元素 \(a_i,a_j\)\((a_i, a_j)\) 能构成逆序对的条件为:
\(i<j, a_i>a_j, t_i > t_j\),或 \(i>j, a_i<a_j, t_i>t_j\)

显然是一个三维偏序的形式。
用 Cdq 统计每个添加次序的贡献,最后对增量进行前缀和, 还原总逆序对数。
两种情况写两个 Cdq 即可。


代码实现

代码中使用了树状数组懒惰删除。
Cdq1, Cdq2 分别解决时间靠后元素较小 / 较大 的情况。

//知识点:CDQ分治 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
#define lowbit(x) (x&-x)
#define mid ((l_+r_)>>1)
const int kMaxn = 1e5 + 10;
//=============================================================
struct Data {
  int x, y, z;
} a[kMaxn], b[kMaxn];
int n, m, val[kMaxn], pos[kMaxn];
int time, ti[kMaxn], t[kMaxn << 1];
ll ans[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void GetMin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
bool Compare(Data fir, Data sec) {
  return fir.x < sec.x;
}
bool CompareCdq1(Data fir, Data sec) {
  return fir.y < sec.y;
}
bool CompareCdq2(Data fir, Data sec) {
  return fir.y > sec.y;
}
void Clear() {
  time ++;
}
void Add(int pos_, int val_) {
  for (; pos_ <= n; pos_ += lowbit(pos_)) {
    if (ti[pos_] < time) {
      t[pos_] = 0;
      ti[pos_] = time;
    }
    t[pos_] += val_;
  }
}
int Sum(int pos_) {
  int ret = 0;
  for (; pos_; pos_ -= lowbit(pos_)) {
    if (ti[pos_] < time) {
      t[pos_] = 0;
      ti[pos_] = time;
    }
    ret += t[pos_];
  }
  return ret;
}
int Query(int l_, int r_) {
  return Sum(r_) - Sum(l_ - 1);
}
void Cdq1(int l_, int r_) {
  if (l_ == r_) return ;
  Cdq1(l_, mid), Cdq1(mid + 1, r_);
  std :: sort(a + l_, a + mid + 1, CompareCdq1);
  std :: sort(a + mid + 1, a + r_ + 1, CompareCdq1);
  int p1 = l_, p2 = mid + 1;
  for (; p2 <= r_; ++ p2) {
    for (; a[p1].y < a[p2].y && p1 <= mid; ++ p1) {
      Add(a[p1].z, 1);
    }
    ans[a[p2].x] += Query(a[p2].z + 1, n);
  }
  Clear();
}
void Cdq2(int l_, int r_) {
  if (l_ == r_) return ;
  Cdq2(l_, mid), Cdq2(mid + 1, r_);
  std :: sort(b + l_, b + mid + 1, CompareCdq2);
  std :: sort(b + mid + 1, b + r_ + 1, CompareCdq2);
  int p1 = l_, p2 = mid + 1;
  for (; p2 <= r_; ++ p2) {
    for (; b[p1].y > b[p2].y && p1 <= mid; ++ p1) {
      Add(b[p1].z, 1);
    }
    ans[b[p2].x] += Query(1, b[p2].z - 1);
  }
  Clear();
}
//=============================================================
int main() {
  n = read(), m = read();
  for (int i = 1; i <= n; ++ i) {
    val[i] = read();
    pos[val[i]] = i;
  }
  for (int i = 1; i <= n; ++ i) {
    a[i] = b[i] = (Data) {0, i, val[i]};
  }
  for (int i = 1; i <= m; ++ i) {
    int del = read();
    a[pos[del]].x = b[pos[del]].x = m - i + 1;
  }
  std :: sort(a + 1, a + n + 1, Compare);
  std :: sort(b + 1, b + n + 1, Compare);
  Cdq1(1, n), Cdq2(1, n);
  for (int i = 1; i <= m; ++ i) ans[i] += ans[i - 1];
  for (int i = m; i >= 1; -- i) printf("%lld\n", ans[i]);
  return 0;
}
posted @ 2020-08-30 22:19  Luckyblock  阅读(175)  评论(2编辑  收藏  举报