题目描述
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
输入输出格式
输入格式:
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
输出格式:
输出包含m行,依次为删除每个元素之前,逆序对的个数。
输入输出样例
输入样例#1: 复制
5 4 1 5 3 4 2 5 1 4 2
输出样例#1: 复制
5 2 2 1 样例解释 (1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。
说明
N<=100000 M<=50000
// 树状数组套动态开点线段树
#include <bits/stdc++.h>
#define ll long long
#define mid (l + r) / 2
using namespace std;
const int mn = 1e5 + 5;
int a[mn], p[mn];
int tr[mn];
struct node
{
int sz;
int ls, rs;
} trie[mn * 40 * 40];
int cnt;
void change(int &id, int l, int r, int num, int gai)
{
if (id == 0) // 空节点 新建
id = ++cnt;
trie[id].sz += gai;
if (l == r)
return;
if (num <= mid)
change(trie[id].ls, l, mid, num, gai);
else
change(trie[id].rs, mid + 1, r, num, gai);
}
int larquery(int id, int L, int l, int r) // 求区间内 >= L 的数的数量
{
if (l == r)
return (L == l) ? trie[id].sz : 0; // L = n + 1 造成的越界返回 0
if (L <= mid)
return trie[trie[id].rs].sz + larquery(trie[id].ls, L, l, mid);
else
return larquery(trie[id].rs, L, mid + 1, r);
}
int smaquery(int id, int R, int l, int r) // 求区间内 <= R 的数的数量
{
if (l == r)
return (R == l) ? trie[id].sz : 0; // R = 0 造成的越界返回 0
if (R <= mid)
return smaquery(trie[id].ls, R, l, mid);
else
return trie[trie[id].ls].sz + smaquery(trie[id].rs, R, mid + 1, r);
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
p[a[i]] = i; // 记录每个数对应的位置
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
for (int j = i; j > 0; j -= j & -j) // 1 ~ (i - 1) 区间内的树包含的 > a[i] 的数的总量
ans += (ll)larquery(tr[j], a[i] + 1, 1, n);
for (int j = i; j <= n; j += j & -j) // (i + 1) ~ n 区间内的树 插入这个数
change(tr[j], 1, n, a[i], 1);
}
while (m--)
{
printf("%lld\n", ans);
int x, LL = 0, RR = 0;
scanf("%d", &x);
for (int i = p[x]; i > 0; i -= i & -i) // 1 ~ (i - 1) 区间的树包含的 > x 的数的总量
LL += larquery(tr[i], x + 1, 1, n);
for (int i = n; i > 0; i -= i & -i) // 当前序列 < x 的数的总量
RR += smaquery(tr[i], x - 1, 1, n);
for (int i = p[x]; i > 0; i -= i & -i) // 1 ~ p[x] 区间的树 < x 的数的总量
RR -= smaquery(tr[i], x - 1, 1, n);
// 两总量相减 = (p[x] + 1) ~ n 区间内 < x 的数的总量
ans -= (ll)(LL + RR); // ans -= 数 x 造成的前后逆序对数
for (int i = p[x]; i <= n; i += i & -i) // (i + 1) ~ n 区间内的树 删除这个数
change(tr[i], 1, n, x, -1);
}
return 0;
}