洛谷 P3157 [CQOI2011]动态逆序对

好像距离上次写题解已经有百年之久了……来更新个吧。

我吹爆 gyh!

题意

给定一个 \(1\sim n\) 的排列,按照题目给定顺序依次删除 \(m\) 个数。

求每次删数前整个序列中的逆序对数。

\(1\le n\le 10^5,1\le m\le 5\times 10^4\)

思路

cdq 分治

将询问离线,并反转序列,将序列删数改为插入。

原问题就转化为了有一个序列 \(a\),每次添加一个数 \(a_i\),并求序列的逆序对数。

对于一个数 \(a_i\),设其被加入的时间为 \(t_i\)。

假设现在有两个数 \((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 分别处理两种情况,也可以用一个 cdq 直接处理两种情况。

还原总逆序对数后记得要逆序输出,因为 \(t_i\) 是倒序处理的。

Luckyblock 教我树状数组懒惰删除秘诀!

代码

#include <bits/stdc++.h>
#define ll long long
#define A 100010
using namespace std;

inline int read() {
  char c = getchar(); int x = 0, f = 1;
  for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
  for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
  return x * f;
}

ll ans[A], t[A];
struct node { int x, y, z; } a[A], b[A];
int n, m, val[A], tim[A], del[A], pos[A], Time;

inline int lowbit(int x) { return (x & -x); }
bool cmp(node fi, node se) { return fi.x < se.x; }
bool cmp1(node fi, node se) { return fi.y < se.y; }
bool cmp2(node fi, node se) { return fi.y > se.y; }

void add(int x, int val) {
  for ( ; x <= n; x += lowbit(x)) {
    if (tim[x] < Time) t[x] = 0, tim[x] = Time;
    t[x] += val;
  }
}

int query(int x, int res = 0) {
  for ( ; x; x -= lowbit(x)) {
    if (tim[x] < Time) t[x] = 0, tim[x] = Time; 
    res += t[x];
  }
  return res;
}

void Cdq1(int l, int r) {
  if (l == r) return;
  int mid = (l + r) >> 1, posr, posl = l;
  Cdq1(l, mid), Cdq1(mid + 1, r);
  sort(a + l, a + mid + 1, cmp1);
  sort(a + mid + 1, a + r + 1, cmp1);
  for (posr = mid + 1; posr <= r; posr++) {
    while (a[posr].y > a[posl].y && posl <= mid)
      add(a[posl].z, 1), posl++;
    ans[a[posr].x] += query(n) - query(a[posr].z);
  }
  Time++;
}

void Cdq2(int l, int r) {
  if (l == r) return;
  int mid = (l + r) >> 1, posr, posl = l;
  Cdq2(l, mid), Cdq2(mid + 1, r);
  sort(b + l, b + mid + 1, cmp2);
  sort(b + mid + 1, b + r + 1, cmp2);
  for (posr = mid + 1; posr <= r; posr++) {
    while (b[posr].y < b[posl].y && posl <= mid)
      add(b[posl].z, 1), posl++;
    ans[b[posr].x] += query(b[posr].z - 1);
  }
  Time++;
}

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].x = 0, a[i].y = i, a[i].z = val[i];
    b[i].x = 0, b[i].y = i, b[i].z = val[i];
  }
  for (int i = 1; i <= m; i++) {
    int now = read();
    a[pos[now]].x = b[pos[now]].x = m - i + 1;
  }
  sort(a + 1, a + 1 + n, cmp);
  sort(b + 1, b + 1 + n, cmp);
  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--) cout << ans[i] << '\n';
  return 0;
}
上一篇:P3157 [CQOI2011]动态逆序对 题解


下一篇:Andoid 的Activity.getWindowManager().getDefaultDisplay().getWidth()已被废弃