现在给出 1 ∼ n 1 \sim n 1∼n 的一个排列,按照某种顺序依次删除 m m m 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
分析
CDQ分治-三维偏序
【操作时间】【数字下标】【数值】
操作时间优先级高,必然排在第一维,对操作时间进行排序
【数字下标】和【数值】优先级相同,任选一个进行分治
考虑删除的操作
删除操作会给逆序对带来的影响怎么来的?
- 在删除操作时间前 前面 存在的大的数
- 在删除操作时间前 后面 存在的小的数
分治不能初始化,将初始的值按照插入进行分治
时间顺序排好,初始化插入操作在前,删除操作依次放
对【数组下标】进行排序,统计前面的大数,后面的小数
细节:
注意到统计小数的时候是左右区间都是从后往前的。为什么呢?
因为在统计前分治左右的时候已经将【数组下标】排好序了
倒过来是为了求,后面(下标大)的小数
这里思考一下,应该就没什么问题了
类似题:CDQ分治专栏
代码
初代版本AC
//P3157
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;
int ar[MAX_N];
int tr[MAX_N];
inline int lowbit(int x) {
return x & -x;
}
inline void add(int x, int k) {
assert(x!=0);
while (x <= N) {
tr[x] += k;
x += lowbit(x);
}
}
inline int ask(int x) {
int res = 0;
while (x) {
res += tr[x];
x -= lowbit(x);
}
return res;
}
struct Qr {
int x, pos, t, v;
}qr[MAX_N];
int sa[MAX_N];
int ans[MAX_N];
void CDQ(int l, int r) {
if (l == r) return;
int mid = l + ((r-l)>>1);
CDQ(l, mid);CDQ(mid+1, r);
int lx = l, rx = mid+1;
for (int i = l; i <= r; ++i) {
if ((lx <= mid && qr[lx].pos <= qr[rx].pos) || rx > r) {
add(qr[lx].x, qr[lx].v);
++lx;
} else {
ans[qr[rx].t] += qr[rx].v * (ask(N) - ask(qr[rx].x));
++rx;
}
}
lx = l, rx = mid+1;
for (int i = l; i <= r; ++i) {
if ((lx <= mid && qr[lx].pos <= qr[rx].pos) || rx > r) {
add(qr[lx].x, -qr[lx].v);
++lx;
} else {
++rx;
}
}
lx = mid, rx = r;
for (int i = l; i <= r; ++i) {
if ((lx >= l && qr[lx].pos >= qr[rx].pos) || rx <= mid) {
add(qr[lx].x, qr[lx].v);
--lx;
} else {
ans[qr[rx].t] += qr[rx].v * (ask(qr[rx].x-1));
--rx;
}
}
lx = mid, rx = r;
for (int i = l; i <= r; ++i) {
if ((lx >= l && qr[lx].pos >= qr[rx].pos) || rx <= mid) {
add(qr[lx].x, -qr[lx].v);
--lx;
} else {
--rx;
}
}
sort(qr+l, qr+r+1, [](Qr a, Qr b){return a.pos < b.pos;});
}
void solve(){
sc("%lld%lld", &N, &M);
int cnt = 0;
int x;
for (int i = 1; i <= N; ++i) {
sc("%lld", &x);
qr[++cnt] = {x, i, cnt, 1};
sa[x] = i;
}
for (int i = 1; i <= M; ++i) {
sc("%lld", &x);
qr[++cnt] = {x, sa[x], cnt, -1};
}
CDQ(1, cnt);
for (int i = 1; i <= cnt; ++i) {
ans[i] += ans[i-1];
}
for (int i = cnt-M; i < cnt; ++i) {
cout << ans[i] << "\n";
}
}
signed main()
{
#ifndef ONLINE_JUDGE
//FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}
优化排序,树状数组
//P3157
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;
int ar[MAX_N];
int tr[MAX_N];
int span[MAX_N];
int tim = 0;
inline int lowbit(int x) {
return x & -x;
}
inline void add(int x, int k) {
assert(x!=0);
while (x <= N) {
if (span[x] != tim) {
span[x] = tim;
tr[x] = 0;
}
tr[x] += k;
x += lowbit(x);
}
}
inline int ask(int x) {
int res = 0;
while (x) {
res += span[x] == tim ? tr[x] : 0;
x -= lowbit(x);
}
return res;
}
struct Qr {
int x, pos, t, v;
}qr[MAX_N], L[MAX_N], R[MAX_N];
int sa[MAX_N];
int ans[MAX_N];
void CDQ(int l, int r) {
if (l == r) return;
int mid = l + ((r-l)>>1);
CDQ(l, mid);CDQ(mid+1, r);
++tim;
int lx = l, rx = mid+1;
int ls = 0;
int rs = 0;
for (int i = l; i <= r; ++i) {
if ((lx <= mid && qr[lx].pos <= qr[rx].pos) || rx > r) {
add(qr[lx].x, qr[lx].v);
L[++ls] = qr[lx++];
} else {
ans[qr[rx].t] += qr[rx].v * (ask(N) - ask(qr[rx].x));
R[++rs] = qr[rx++];
}
}
++tim;
lx = mid, rx = r;
for (int i = l; i <= r; ++i) {
if ((lx >= l && qr[lx].pos >= qr[rx].pos) || rx <= mid) {
add(qr[lx].x, qr[lx].v);
--lx;
} else {
ans[qr[rx].t] += qr[rx].v * (ask(qr[rx].x-1));
--rx;
}
}
for (int i = 1; i <= ls; ++i) {
qr[l+i-1] = L[i];
}
for (int i = 1; i <= rs; ++i) {
qr[l+ls+i-1] = R[i];
}
}
void solve(){
sc("%lld%lld", &N, &M);
int cnt = 0;
int x;
for (int i = 1; i <= N; ++i) {
sc("%lld", &x);
qr[++cnt] = {x, i, cnt, 1};
sa[x] = i;
}
for (int i = 1; i <= M; ++i) {
sc("%lld", &x);
qr[++cnt] = {x, sa[x], cnt, -1};
}
CDQ(1, cnt);
for (int i = 1; i <= cnt; ++i) {
ans[i] += ans[i-1];
}
for (int i = cnt-M; i < cnt; ++i) {
cout << ans[i] << "\n";
}
}
signed main()
{
#ifndef ONLINE_JUDGE
//FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}