题目大意
给你n个商品,对于每个商品可以选择三个操作之一: 1.以这个商品价格买入这个商品 2.如果之前买过某商品以这个商品价格卖出一个商品 3.什么也不做
解题思路
先讲一个别的问题。对于A, B, C, D四个数,任你选出几对数求最大差值, 如果(A<B<C, B>D),是有两种选择方法的,一种是C-A,一种是D-A, C-B,显然是前者更优,因为B>D,所以第二种比第一种少了B-D。这也就证明了一个结论:对于当前这个数来说,一定是选前面最小的数更优。
回到本题,我们从左到右扫过每一个数,对于第一次决策,如果前面存在一个最小的数比当前的数小,答案就加上他们的差值,如果后面有一个更大的数呢?有两种选择,可以再选一个前面最小的没有在决策里的数加上他们的差值,也可以替换掉前一个方案,你可能会问,如果我替换掉前面的方案,被替换方案中的数不是没法再跟他前面的数相减了吗?如果之前的决策能被替换,可以参考上面的情况,其肯定是满足(B geq D)的,其中D是我们前面的最小值,B是决策中要被替换的数,这样的话就转换成了上面的问题了。
具体做法是,我们用一个multiset S来维护前面可选的最小的数,用一个优先队列q来维护决策中能被替换的最小的数,然后从左到右扫一遍序列,如果S和q为空,说明没有方案可选,将其加进S中。如果S不为空,q为空,说明没有替换方案,就从S中选一个最小的数a[j],然后把决策a[i], a[j]加入q中并计算贡献(q以a[i]为第一关键字从小到大排序)。如果S不为空,q不为空,则贪心的选择替换或者进行新的决策,如果替换方案的话,原先的a[i]是可以再利用的,再将其加入S中。如果S为空,q不为空,那就只能考虑是否替换更优的决策了。
代码
const int maxn = 2e5+10;
const int maxm = 1e7+10;
int a[maxn];
int main() {
int __; scanf("%d", &__);
while(__--) {
int n; scanf("%d", &n);
priority_queue<P, vector<P>, greater<P> > pq;
multiset<int> st;
ll sum = 0, cnt = 0;
for (int i = 1; i<=n; ++i) {
scanf("%d", &a[i]);
if (st.empty() && pq.empty()) st.insert(a[i]);
else if (!st.empty()) {
if (!pq.empty() && a[i]-pq.top().x>0 && a[i]-pq.top().x>=a[i]-*st.begin()) {
P t = pq.top(); pq.pop();
sum += a[i]-t.x;
pq.push({a[i], t.y});
st.insert(t.x);
}
else if (*st.begin()<a[i]) {
sum += a[i]-*st.begin();
cnt += 2;
pq.push({a[i], *st.begin()});
st.erase(st.begin());
}
else st.insert(a[i]);
}
else if (!pq.empty()) {
if (a[i]>pq.top().x) {
P t = pq.top(); pq.pop();
sum += a[i]-t.x;
pq.push({a[i], t.y});
st.insert(t.x);
}
else st.insert(a[i]);
}
}
cout << sum << ' ' << cnt << endl;
}
return 0;
}