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;
}
作者@Luckyblock,转载请声明出处。