跳转链接
艾洛喜欢吃甜食,他有n个甜甜圈,现在叠成了两叠(如下图所示),第一叠有n1个,第二叠有n2个(n1+n2=n),要解决的问题如下:
- 每个甜甜圈都有一个唯一的甜度值\(s_i\)
- 甜度值两两不同;
每次艾洛可以把任意一叠位于顶端的一个甜甜圈移动到另一叠顶端,若该甜甜圈是当前所有甜甜圈中最甜的(甜度值最大),那么艾洛不会移动甜甜圈,而是直接吃掉;
请你求出艾洛吃完所有甜甜圈的最小移动步数;
样例输入 3 3 1 4 5 2 7 3 样例输出 6
我们可以把两叠甜甜圈从顶端拼接成一个甜甜圈.
问题就转化为了按照从大到小的顺序去移动一个指针(指针的初始点在两个甜甜圈的顶端位置)
这个问题可以用树状数组解决
我们模拟一下样例
3 7 2 | 1 4 5 (刚开始的时候指针在中间)
3 | 2 1 4 5 然后指针要移动到最大值(7) 移动了 一次
3 2 1 4 | 然后指针要移动到最大值(5) 移动了 三次
3 2 1 | 然后指针要移动到最大值(4) 移动了 零次
| 2 1 然后指针要移动到最大值(3) 移动了 两次
| 1 然后指针要移动到最大值(2) 移动了 零次
| 然后指针要移动到最大值(1) 移动了 零次
因此呢,问题就转化为了求从最大值到最小值按顺序移动最少需要移动多少次的问题
我们只需要对数组按照大小排一下序,然后用树状数组求得即可
#include <cstdio> #include <algorithm> #define LL long long #define lowbit(x) x & -x #define fi first #define se second #define PII pair<int, int> using namespace std; const int N = 1e5 + 10; int n, m, len, tr[N]; PII a[N]; inline void add(int x, int c) { while(x <= len) { tr[x] += c; x += lowbit(x); } } inline int ask(int x) { int res = 0; while(x) { res += tr[x]; x -= lowbit(x); } return res; } int main() { scanf("%d%d", &n, &m); len = n + m; for(int i = m + 1; i <= len; i ++ ) { int x; scanf("%d", &x); add(i, 1); a[i] = {x, i}; } for(int i = m; i; i -- ) { int x; scanf("%d", &x); add(i, 1); a[i] = {x, i}; } sort(a + 1, a + 1 + len); reverse(a + 1, a + 1 + len); a[0].se = m; LL ans = 0; for(int i = 1; i <= len; i ++ ) { add(a[i].se, -1); //这里先把它减少就可以避免一个小问题 如果指针和当前的最大值相邻的话,就直接不用考虑移动了 ans += abs(ask(a[i].se) - ask(a[i-1].se)); } printf("%lld\n", ans); return 0; }