题目:给n个数对<a,b>表示个数的数组;
每次操作:
删除第一个数
删除两个不同数的后一个
问:对于前i个数对的数组,多少次操作后可以清空数组?
思路:观察特点可以看到ans是非递减的,同时给出模拟的想法:对于前i个数对,每次取出最小的ai的数对,对于前i个数,全部都更新一遍,同时维护取出ai数对位置两边的合并信息,上述操作执行i次得到结果,发现复杂度为。可以观察到,我们维护信息大量重复的为:
前i个数对的合并信息;我们尝试维护如下信息:使用完第i-1个数对的合并信息,这样在i的数对时可以通过使用如上信息进行维护;
步骤如下:
1.处理第i个数对时,假设我们处理出i-1数对的合并信息,当我们需要使用i-1数对的合并信息时,第i个数对必须突破第i-1的数对,这里的突破可以为合并或者删除;
2.当时,无法突破,那么{}的信息需要添加进第i-1数对的合并信息中,变为第i个数对的合并信息;ans+=a;
3.否则突破后=,对于i-1的合并信息,当遇到与bi相同的信息时跳过,否则尝试突破,直到结束或者ai<=0;
4.初始时,对于a1,第0个数对的合并信息为空;
通过如上步骤分析:我们可以采用vector来维护信息:
对于无法突破时:更新i-1的合并信息,添加{ai,bi}到i-1的合并信息中;
对于突破时:1.对于b不同时,减去ai,当某个a为空时,弹出。
2.对于b相同时,弹出vector,将信息全部累加到一个临时变量中,最后添加到合并信息中。
可以知道,当无法突破时,就退出循环;
实现代码如下:
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using namespace std;
void solve() {
int n;
cin >> n;
vector<pair<int, i64>> stk;
i64 ans = 0;
for (int i = 0; i < n; i++) {
int a, b;
cin >> a >> b;
i64 len = 0;
int ta=a;
while (!stk.empty() && (a > 0 || stk.back().first == b)) {
if (stk.back().first == b) {
len+=stk.back().second;
stk.pop_back();
} else {
i64 t = std::min<i64>(a, stk.back().second);
a -= t;
stk.back().second -= t;
if (stk.back().second == 0) {
stk.pop_back();
}
}
}
if(stk.empty()) ans += a;
if(len!=0) stk.emplace_back(b, len);
stk.emplace_back(b, ta);
cout << ans << " \n"[i == n - 1];
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}