题意:
给你了两个堆,你可以随便选择一个堆顶的元素,将它放在另外一个堆上面,如果堆顶的元素的优先级是所有元素中的最大的,那么我们就可以将它删除,问你最少需要多少步能够将所有的元素删除。
思路
我们将两个堆按照堆顶合并起来,假如给你的两个堆分别是(从堆顶到堆底)1、4、5。2、7、3,合并之后是:5、4、1、2、7、3。这样我们可以看成一个数组,但是其实位置该怎样判断呢,我们可以在两个堆顶之间加一个元素,标记为起点,就是5、4、1、0、2、7、3。这样我们可以从0这个位置开始进行寻找,那么现在数组的长度是7,我们标记一下我们的起点位置s = 4(就是0这个位置),然后这个时候我们只需要寻找最大值,次大值,次次大值…即可。
我们现在来拟一下这个操作过程:首先我们需要一个数组来记录出现的这些数以及他们的顺序,在这个过程中就add,因为在后面我们要算当前位置和下个最大值之间的距离,并且我们是从最大值开始寻找的,所以这里得到的数组我们需要从大到小进行排序,后面直接遍历即可。
我们在来想一下遍历的过程,我们是从大到小的顺序来遍历的,我们要找这个最大值的位置,那么我们在输入的时候还需要记录每一个值的id,先来看一下代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m;
int tr[N];
struct node
{
int idx, val;
bool operator < (const node &t) { return val > t.val;}
}a[N];
int lowbit(int x) { return x & -x; }
void add(int x, int k) { for (int i = x; i <= m + n + 1; i += lowbit(i)) tr[i] += k; }
int sum(int x)
{
int sum = 0;
for (int i = x; i; i -= lowbit(i)) sum += tr[i];
return sum;
}
signed main()
{
std::ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = n; i >= 1; i --) { cin >> a[i].val; a[i].idx = i; add(i, 1); }
for (int i = n + 2; i <= n + m + 1; i ++) { cin >> a[i].val; a[i].idx = i; add(i, 1); }
sort(a + 1, a + n + m + 2);
int ans = 0;
int top = n + 1;
for (int i = 1; i <= n + m; i ++)
{
int cur = a[i].idx;
if (top < cur) { ans += sum(cur - 1) - sum(top); top = cur - 1; add(cur, -1); }
else { ans += sum(top) - sum(cur); top = cur; add(cur, -1); }
}
cout << ans << endl;
return 0;
}
现在来解释一下循环中的if else的原因:
我们要考虑指针位置和当前最大值的位置情况:
1.假如指针在当前最大值的左边:# # # 0 # m # (0表示指针的位置,m表示当前最大值的位置),那么我们知道此时只需要移动一个数字,然后就可以将最大值删除掉,所以我们要找的是sum(m-1),而不是sum(m),因为我们只需要算他们之间隔了多少个。
2.假如指针在当前最大值的右边:# m # # # 0 #,我们可以看出来它们之间需要移动三个单位,移动了之后最大值就暴露出来了,就可以直接删除了,所以是sum(top) - sum(cur)。