codeforces 702div3 题解

本文作者参加了Codeforces 703 div3比赛,分享了一小时内解答六题的经验,并详细解析了涉及单调栈、二分查找和数学推导的G题。尽管因误解题意未能完全解决第七题,但通过分析,作者提供了针对这类问题的解题思路,包括如何利用单调栈维护前缀和,并结合二分查找寻找满足条件的最小循环次数。此外,作者还提到比赛策略和休息调整的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前天打了codeforces703div3的比赛,这场比赛我一个小时做出了前6题,第7题在场内因为看错了题意,很遗憾没能AK,从200名掉到了800多名,在算排名的人里面排了200多名,涨了差不多100分吧。我codeforces有两个账号, 一个1800分,一个1600多分。今天的cf我没有打,先休息一天吧,下周在继续冲击更高的目标。

G题:(单调栈 + 二分 + 数学推导)

前面六个题都很基础,就不讲解了,重点讲一下G题吧。
G题是让你求一个长度为n的循环序列至少多少项的前缀和大于等于x。
n是200000,询问也是200000。
很显然,我们要先对长度为n的一个循环节求前缀和。对于一个可能很大的x我们还要考虑这个x至少需要多少个循环节才能达到题目要求。
比如一个长度为n的序列我们设为:
-1 2 4 -1 11
我们求它的前缀和数组为:
-1 1 5 4 15
我们发现4这个数,只需要3个数的前缀和就可以达到了,所有4应该是一个不可达状态。
就是说,对于当前前缀和为x,向后遍历的时候,前缀和为y,只有当y > x才是一个应该被记录的状态。
维护这种需要,我们可以考虑一个数据结构:单调栈。
我们把上述序列的-1, 1, 5, 15存到一个单调栈中。
我们维护好一个单调栈就可以对题目中的x进行查找了,注意:(题目中没每一个询问都是>=1的,这其实很重要):
我们可以先对x在我们维护好的单调栈中进行二分查找,比如x是6,在[-1, 1, 5, 15]单调栈中,至少得15这个状态可以满足条件。如果能查找到答案,直接输出即可。
否则,如果一个循环节的前缀和sum[n]是小于等于0的话,我们不可以通过多次循环节的方式去找到答案,所以输出-1。
最后还有一种情况就是一个循环节的sum[n] > 0,x在第一个循环节中不能找到答案。这种情况,需要多次循环节进行完成,我们需要找到这个x至少需要多少个循环节才能满足条件,同时求出最后一个循环节中至少要保证大于等于几,在最后一个循环节(也就是对一个循环节开的单调栈)中进行一下二分查找即可。此处的数学推导要注意细节,不然会wrong answer。
(比赛的时候,不知到什么原因,把题目里的大于等于想成等于了,因此得出的办法是不能通过本题的)

代码如下:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll inf = 1e18;
int _;
int n, m;
ll a[200010];
ll sum[2000010];
vector< pair<ll, int> > stk;

void solve(){
    cin >> _;
    while(_--){
        cin >> n >> m;
        sum[0] = 0;
        for(int i = 1; i <= n; i++){
            cin >> a[i];
            sum[i] = sum[i-1] + a[i];
        }
        ll maxx = -inf;
        stk.clear();
        for(int i = 1; i <= n; i++){
            if(sum[i] > maxx){
                stk.push_back({sum[i], i - 1});
                maxx = sum[i];
            }
        }
        ll d = sum[n];
        while(m--){
            ll x;
            cin >> x;
            int k = lower_bound(stk.begin(), stk.end(), make_pair(x, 0)) - stk.begin();
            if(k < stk.size()){
                cout << stk[k].second << " ";
                continue;
            }
            if(d <= 0){
                cout << "-1" << " ";
                continue;
            }
            ll tim = (x - maxx - 1)/d + 1;
            k = lower_bound(stk.begin(), stk.end(), make_pair(x-tim*d, 0)) - stk.begin();
            cout << stk[k].second + tim*n << " ";
        }
        cout << "\n";
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值