链接:https://ac.nowcoder.com/acm/contest/17148/J
来源:牛客网
艾洛喜欢吃甜食,他有n个甜甜圈,现在叠成了两叠(如下图所示),第一叠有n1个,第二叠有n2个(n1+n2=n),要解决的问题如下:
- 每个甜甜圈都有一个唯一的甜度值sis_isi,甜度值两两不同;
- 每次艾洛可以把任意一叠位于顶端的一个甜甜圈移动到另一叠顶端,若该甜甜圈是当前所有甜甜圈中最甜的(甜度值最大),那么艾洛不会移动甜甜圈,而是直接吃掉;
输入
3 3 1 4 5 2 7 3
输出
6
首先拿到这道题目一开始的想法绝对是暴力,但是我们很容易发现暴力会被卡成n2(max1, maxn-1 ...... maxn-2, max2)就是将最大最小交替放置。
考虑优化。我们真的需要知道所有元素的绝对顺序么?似乎是不必要的。我们只需要知道当前最大元素上面有多少个元素即可,以及判断这个最大元素是在左区间还是右区间。
那么求出当前元素之前有多少个元素该如何求呢?考虑树状数组。树状数组可以在logn时间内求出当前元素之前有多少个元素,而且支持单点修改。非常适合这道题目。
那么左右区间该如何个维护呢。我们稍微模拟一下这个过程可以发现,每次我们都会将当前最大的元素作为左右区间的分割点。那么我们只需要在每次算出贡献之后把区间的分割点修改即可。
但是考虑到左右区间都是自顶向上,所以在存左区间时需要倒序存储。
具体代码如下:
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5+10; typedef long long ll; int n, m; int tree[maxn]; int lowbit(int x) { return x & (-x); } void add(int pos, int val) { for (pos ; pos < maxn; pos += lowbit(pos)) { tree[pos] += val; } } int query(int pos) { int sum = 0; for (pos ; pos >= 1; pos -= lowbit(pos)) { sum += tree[pos]; } return sum; } struct node { int val, id; } a[maxn]; bool cmp(node a1, node a2) { return a1.val > a2.val; } int main() { scanf("%d%d", &n, &m); int cnt = 0; for (int i = n; i >= 1; -- i) { a[++ cnt].id = i; scanf("%d", &a[cnt].val); } for (int i = 1; i <= m; ++ i) { a[++cnt].id = i + n; scanf("%d", &a[cnt].val); } sort(a+1, a+1+cnt, cmp); ///大到小排序 int bound = n; ///划分左右区间 for (int i = 1; i <= cnt; ++ i) { add(i, 1); } ll ans = 0; for (int i = 1; i <= cnt; ++ i) { int p = a[i].id; if (p > bound) { ///右区间 ans += query(p) - query(bound) - 1; } else { ///左区间 ans += query(bound) - query(p); } add(p, -1); bound = p; ///修改分割点 } printf("%lld ", ans); return 0; }