首页 > 其他分享 >codeforces 1041 C. Coffee Break

codeforces 1041 C. Coffee Break

时间:2024-09-22 22:03:48浏览次数:11  
标签:1041 int ne 复杂度 codeforces st Break leq 数组

题意

第一行输入三个整数 \(n,m,d(1 \leq n \leq 2 * 10^5, n \leq m \leq 10^9, 1 \leq d \leq n)\),第二行输入 \(n\) 个整数,保证每个数均不大于 \(m\)。
在每一天你都可以任意选择一个未选过的数 \(a_i\),随后可以继续选任意一个大于 \(a_i + d\) 的数 \(a_j\);接下来可以再选任意一个大于 \(a_j + d\) 的数 \(a_k\);最后重复执行上述步骤。
问:最少需要多少天,可以选完全部的数?

题解

先给结论:先从小到大排序,然后选中剩下的数组的第一个数 \(a_0\),随后选中数组中第一个满足 \(a_i \geq a_0 + d + 1\) 的数 \(a_i\),下一步再选中数组中第一个满足 \(a_j \geq a_i + d\) 的数 \(a_j\),形成的子数组数目便是答案。

疑问解析:为什么优先选择最小的满足 \(a_j \geq a_i + d + 1\)的数是正确的?
答:不妨假设数组排序后为 \(a_0, a_1, ..., a_i, a_j, ..., a_n\),且 \(a_i\) 是最小的满足大于 \(a_0 + d\) 的数。那么明显在选中 \(a_0\) 的下一步,既可以选择 \(a_i\),也可以选择 \(a_j\)。此时若 \(a_j - a_i \leq d\),那明显二者至多只能够选其一,那么无论选择哪一个,都必然意味着需要另外再选择出一个子数组。但如果 \(a_j - a_i \geq d + 1\),就可以先选 \(a_i\) 而后一并选上 \(a_j\)。因此,先选较小者更优。

接下来分析具体代码实现:
如何在数组中找出第一个比选中的数大 \(d\) 以上的数呢?
明显可以线性查找,但是当数组中任意两个数的差值均小于 \(d\) 时,时间复杂度为 \(O(n^2)\),明显不符合要求
观察我们的前置条件排序,所以可以考虑使用二分法来使得查找的时间复杂度降低为 \(O(logn)\)
如何选择过的数从数组中移除呢?
若通过类似插入排序等思路,时间复杂度为 \(O(n^2)\),明显不符合要求

此时思考:既要排序,又要可以二分,还要支持快速删除,我们可以联想到红黑树的性质,但是手撕红黑树太硬核了,可以借助 \(set\) 或 \(map\) 实现,这二者的查找/插入/删除/修改操作时间复杂度都是 \(O(logn)\),符合时间复杂度要求。

参考代码

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
using namespace std;

constexpr int INF = 0x3f3f3f3f;
constexpr int N = 2e5 + 7;
int n, m, d, cnt, a;
int ans[N];

int main() {
    IOS
    set<PII> st;
    cin >> n >> m >> d;
    for (int i = 0; i < n; ++ i) {
        cin >> a;
        st.insert(PII(a, i));
    }
    while (!st.empty()) {
        auto f = st.begin();
        int key = (*f).first;
        ans[(*f).second] = ++ cnt;
        st.erase(f);
        while (!st.empty()) {
            auto ne = st.upper_bound(PII(key + d, INF));
            if (ne != st.end()) {
                key = (*ne).first;
                ans[(*ne).second] = cnt;
                st.erase(ne);
            } else break;
        }
    }
    cout << cnt << '\n';
    for (int i = 0; i < n; ++ i) cout << ans[i] << ' ';
    return 0;
}

标签:1041,int,ne,复杂度,codeforces,st,Break,leq,数组
From: https://www.cnblogs.com/RomanLin/p/18425870

相关文章

  • Codeforces Round 974 (Div. 3)
    CodeforcesRound974(Div.3)A-RobinHelps按题目要求一步步计算就行#include<bits/stdc++.h>usingnamespacestd;intn,k;voidsolve(){ cin>>n>>k; intsum=0,num,ans=0; for(inti=1;i<=n;++i){ cin>>num; if(num&g......
  • Codeforces Round 974 (Div. 3)
    A.RobinHelps模拟即可。B.RobinHoodandtheMajorOak注意到\(i^i\equivi\pmod2\),因此\(\sumi^i\equiv\sumi\pmod2\)。等差数列求和即可。C.RobinHoodinTown二分答案即可。D.RobertHoodandMrsHood枚举区间\([l,l+d-1]\)。此时我们需要快速......
  • 【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解
    【洛谷】P10417[蓝桥杯2023国A]第K小的和的题解题目传送门题解CSP-S1补全程序,致敬全A的答案,和神奇的预言家。写一下这篇的题解说不定能加CSP2024的RP代码#include<bits/stdc++.h>#definelowbit(x)x&(-x)#defineendl"\n"usingnamespacestd......
  • Leetcode 1041. 困于环中的机器人
    1.题目基本信息1.1.题目描述在无限的平面上,机器人最初位于(0,0)处,面朝北方。注意:北方向是y轴的正方向。南方向是y轴的负方向。东方向是x轴的正方向。西方向是x轴的负方向。机器人可以接受下列三条指令之一:“G”:直走1个单位“L”:左转90度“R”......
  • Codeforces Round 974 (Div. 3)
    A:按题意模拟。B:\(i^i\)与\(i\)奇偶性相同,求\((n-k,n]\)的奇数个数。C:二分答案。D:即求每个\((i-d,i]\)有多少线段覆盖。扫到\(i\)时加入所有\(i=l\)的,弹出所有\(r\lei-d\)的。E:枚举相遇点,答案就是\(\min\big(\max(d_1,d_2)\big)\),最短路时记录状态......
  • Codeforces Round 973 (Div. 2)
    CodeforcesRound973(Div.2)A-Zhan'sBlender由于总量固定因此只用计算两个处理时间最大值即是所求#include<bits/stdc++.h>usingnamespacestd;intn;doublex,y;voidsolve(){ cin>>n; cin>>x>>y; inttim1=ceil(n*1.0/x); inttim2......
  • Codeforces Round 974 (Div. 3)
    A.RobinHelps题意:Robin一开始的钱为0,每个人都有ai个金币,如果ai>=k则Robin会拿着它的金币, 如果ai==0且手上有金币,Robin会送出1金币,输出Robin送了几次思路:按照题意Code:#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;usingi6......
  • Codeforces Round 973 (Div. 2)
    SolCF2013A每次最多操作\(\min(x,y)\),故答案为\(\lceil\frac{n}{\min(x,y)}\rceil\)。#include<bits/stdc++.h>usingnamespacestd;usingu32=unsigned;usingi64=longlong;usingu64=unsignedlonglong;usingi128=__int128;#defineIOS#de......
  • Codeforces Round 973 (Div. 2) B. Battle for Survive
    题目链接:题目大意:把题目的操作翻译一下就是拿一个数去减后面的一个数,然后前面这个数会消掉。最小化最后剩下的数。思路:容易看出,最后剩下的一定是最后一个数,因为最后一个数一定不会被消去,又已知最后只剩下一个数,那么就是最后一个数。前面的所有数都要被消去,最差的情况就......
  • Educational Codeforces Round 135 (Rated for Div. 2)D. Letter Picking
    注意读题,每次拿完之后是放在开头。所以先手不败,因为最后剩下两个的时候,先手一定可以取较小值。考虑怎样会出现平局?首先已经知道了先手不败,那么对于后手来说,他追求的就是平局,也就是尽可能的保证每一步都都与先手相同。所以,如果是回文串,或者两两相同,或者回文串包两两相同的情况,才......