标签:树状数组(求逆序对)
有点套路
题意
在一段序列中,每次选定一个数,将这个序列中小于这个数的所有数排序,并顺序插入它们原来的位置上。(本次操作会对下一次影响)
问每次操作完了之后(和没操作时)的逆序对个数。
这道题目的逆序对意思有点奇怪,刚开始的逆序对包括相等的两个数。
思路
以下操作 (n) 指对第 (n) 个数进行上述操作
先离散化。
对于一个较大数的操作,是可以从较小的数的操作推导过来的,我们可以这样想:
假设现在已经对于数操作 (n) 完了,那么对于操作 (n+1) ,它相对于上一个操作减少的逆序对只是在每个数 (n+1) 后面小于这个数的个数和。
可以这样理解:
-
第一种逆序对,是两个小于 (n+1) 的数组成,显然已经没有了,因为操作n已经去除了。
-
第二种逆序对,是一个数 (n+1) 和小于 (n+1) 的数组成,这种会减少(排序一下,小的数到它前面了),也是要计算的。
-
第三种逆序对,是一个数 (n+1) 和大于 (n+1) 的数组成,这可能会改变,但是操作过后每个数 (n+1) 的位置上会变为小于等于自己的数,和大于 (n+1) 组成的逆序对个数一样,因此,并未改变。
-
第四种逆序对,是两个大于 (n+1) 的数组成,显然个数不变。
理清了思路,发现第二种逆序对正好可以用树状数组动态解决,操作 (n) 过后,维护小于等于 (n) 个数的前缀。
并且注意,它的操作是会延续到下一次的,因此注意下下一次操作小于本次操作。
时间复杂度 ( exttt{O(N logN)})。
注意会暴 ( exttt{int})。
代码
#include <bits/stdc++.h>
using namespace std;
#define PB push_back
#define int long long
const int N = 1e6;
inline int read()
{
int s = 0;
register bool neg = 0;
register char c = getchar();
for (; c < '0' || c > '9'; c = getchar())
neg |= (c == '-');
for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
;
s = (neg ? -s : s);
return s;
}
int a, b, s[N + 10], p[N + 10], top, Ans[N + 10], q[N + 10], t[N + 10];
vector<int> id[N + 10];
inline void add(int n, int m)
{
for (; n <= a; n += n & -n)
q[n] += m;
}
inline int query(int n)
{
int ans = 0;
for (; n; n -= n & -n)
ans += q[n];
return ans;
}
signed main()
{
a = read();
b = read();
for (int i = 1; i <= a; i++)
s[i] = p[i] = read();
sort(p + 1, p + a + 1);
top = unique(p + 1, p + a + 1) - p - 1;
for (int i = 1; i <= a; i++)
s[i] = lower_bound(p + 1, p + top + 1, s[i]) - p;
int ans = 0, qq = 0;
for (int i = 1; i <= a; i++)
{
add(s[i], 1);//求一开始的逆序对。
qq += t[s[i]];
ans += i - query(s[i]);
t[s[i]]++;
}
memset(q, 0, sizeof(q));
ans += qq;
printf("%lld
", ans);
for (int i = 1; i <= a; i++)
id[s[i]].PB(i);
int sum = 0;
for (int i = 1; i <= top; i++)
{
for (int j = 0; j < id[i].size(); j++)
{
ans -= sum - query(id[i][j]);
}
ans -= id[i].size() - 1;//应对题目奇奇怪怪的逆序对定义。
for (int j = 0; j < id[i].size(); j++)
{
add(id[i][j], 1);//维护小于等于n个数的前缀
Ans[id[i][j]] = ans;//储存答案
}
sum += id[i].size();
}
int x;
int mx = 1e18;
for (int i = 1; i <= b; i++)
{
x = read();
mx = min(mx, Ans[x]);
printf("%lld
", mx);
}
return 0;
}