题目链接:
Click here
Solution
设
v
a
l
i
val_i
vali表示
i
i
i与它后面的数形成的逆序对个数。
a
n
s
ans
ans为原来数列逆序对个数。
每次询问一个
x
x
x,对于满足
i
>
=
x
i>=x
i>=x并且
a
i
<
=
a
x
a_i<=a_x
ai<=ax的所有
i
i
i,若
j
>
i
j>i
j>i并且
a
j
<
a
i
a_j<a_i
aj<ai,那么
(
i
,
j
)
(i,j)
(i,j)形成了逆序对。但由于
a
j
<
a
i
<
=
a
x
a_j<a_i<=a_x
aj<ai<=ax,所以这些
j
j
j都会被排好序,变成了
j
>
i
j>i
j>i并且
a
j
>
a
i
a_j>a_i
aj>ai,这些
i
i
i的逆序对也就全都被消去了,也就是说
a
n
s
ans
ans要减去这些
v
a
l
i
val_i
vali。
直接上树套树orCDQ?不存在的。因为一个
i
i
i在某一次询问被消去影响之后,就不会再被消去,所以在线的做法没法做。那么考虑离线,按照询问的修改位置排序,询问
x
x
x能够消去位置
i
i
i的影响的条件是
x
<
=
i
x<=i
x<=i且
a
i
<
=
a
x
a_i<=a_x
ai<=ax。设
t
x
t_x
tx表示
x
x
x是第几个询问,那么
i
i
i最早消去影响的时间就是满足
x
<
=
i
x<=i
x<=i且
a
i
<
=
a
x
a_i<=a_x
ai<=ax的最小的
t
x
t_x
tx。
这是个二维偏序问题,按照
x
x
x排序后一维被消掉,条件
a
i
<
=
a
x
a_i<=a_x
ai<=ax用树状数组维护后缀最小值即可。
至于当询问
y
y
y对应的
a
y
a_y
ay被某个询问
x
x
x修改的情况,我们只需要用原来没被修改过的的
a
y
a_y
ay即可,因为如果
y
y
y被
x
x
x改了,说明
y
y
y后面的逆序对早就被询问
x
x
x消完了,我们即使把这个询问加进去也不会影响答案。
然后此题完。
Code
由于using namespace std
被ban掉了,为了巩固基础知识,我自己实现了归并排序、离散化、二分等算法,导致码量大了一点,但是让我对基础算法理解加深了更多。
#include <cstdio>
#include <cstring>
#include <cstdlib>
typedef long long ll;
const int N = 100007;
const ll INF = 0x3f3f3f3f;
int min(int a, int b) { return a < b ? a : b; }
int n, m, len, a[N], val[N], arr[N], temp[N];
ll sum[N];
struct note { int po, tim, val; } q[N], tmp[N];
int operator<(note a, note b) { return a.po < b.po; }
void sortq(int l, int r)
{
if (l == r) return;
int mid = l + r >> 1;
sortq(l, mid), sortq(mid + 1, r);
int i = l, j = mid + 1, len = 0;
while (j <= r)
{
while (i <= mid && q[i] < q[j]) tmp[++len] = q[i++];
tmp[++len] = q[j++];
}
while (i <= mid) tmp[++len] = q[i++];
for (int i = 1; i <= len; i++) q[l + i - 1] = tmp[i];
}
void sort(int l, int r)
{
if (l == r) return;
int mid = l + r >> 1;
sort(l, mid), sort(mid + 1, r);
int i = l, j = mid + 1, len = 0;
while (j <= r)
{
while (i <= mid && arr[i] <= arr[j]) temp[++len] = arr[i++];
temp[++len] = arr[j++];
}
while (i <= mid) temp[++len] = arr[i++];
for (int i = 1; i <= len; i++) arr[l + i - 1] = temp[i];
}
void unique()
{
len = 0;
int i = 1;
while (i <= n)
{
arr[++len] = arr[i++];
while (i <= n && arr[i] == arr[i - 1]) i++;
}
}
int lower_bound(int val)
{
int l = 1, r = len, mid, ans;
while (l <= r)
{
mid = l + r >> 1;
if (arr[mid] >= val) r = mid - 1, ans = mid;
else l = mid + 1;
}
return ans;
}
int tr[N][2];
void add(int po, int k, int v)
{
for (; po <= n; po += (po & (-po)))
{
if (k) tr[po][k] = min(tr[po][k], v);
else tr[po][k] += v;
}
}
int query(int po, int k)
{
int ret = k ? INF : 0;
for (; po; po -= (po & (-po)))
{
if (k) ret = min(ret, tr[po][k]);
else ret += tr[po][k];
}
return ret;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", a + i);
memcpy(arr, a, sizeof(a));
sort(1, n);
unique();
for (int i = n; i >= 1; i--) a[i] = lower_bound(a[i]), val[i] = query(a[i] - 1, 0), add(a[i], 0, 1), sum[0] += val[i];
for (int i = 1; i <= m; i++) scanf("%d", &q[i].po), q[i].tim = i, q[i].val = a[q[i].po];
sortq(1, m);
for (int i = 1; i <= n; i++) tr[i][1] = INF;
for (int i = 1, j = 1; i <= n; i++)
{
while (j <= m && q[j].po <= i) add(len - q[j].val + 1, 1, q[j].tim), j++;
int ret = query(len - a[i] + 1, 1);
if (ret < INF) sum[ret] -= val[i];
}
printf("%lld\n", sum[0]);
for (int i = 1; i <= m; i++) sum[i] += sum[i - 1], printf("%lld\n", sum[i]);
return 0;
}