CF2002E题解

题目:给n个数对<a,b>表示\sum ai个数的数组;

每次操作:

删除第一个数

删除两个不同数的后一个

问:对于前i个数对的数组,多少次操作后可以清空数组?

思路:观察特点可以看到ans是非递减的,同时给出模拟的想法:对于前i个数对,每次取出最小的ai的数对,对于前i个数,全部都更新一遍,同时维护取出ai数对位置两边的合并信息,上述操作执行i次得到结果,发现复杂度为n^{2}。可以观察到,我们维护信息大量重复的为:

前i个数对的合并信息;我们尝试维护如下信息:使用完第i-1个数对的合并信息,这样在i的数对时可以通过使用如上信息进行维护;

步骤如下:

1.处理第i个数对时,假设我们处理出i-1数对的合并信息,当我们需要使用i-1数对的合并信息时,第i个数对必须突破第i-1的数对,这里的突破可以为合并或者删除;

2.当a_{i-1}>=a_{i}时,无法突破,那么{a_{i-1},b_{i-1}}的信息需要添加进第i-1数对的合并信息中,变为第i个数对的合并信息;ans+=a;

3.否则突破后a_{i}=a_{i}-a_{i-1},对于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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值