P8685 [蓝桥杯 2019 省 A] 外卖店优先级 题解

发现两篇题解都使用的是暴力模拟,时间复杂度为 O ( N ⋅ T ) O(N\cdot T) O(NT) 的算法,题目数据范围 1 ≤ N , M , T ≤ 1 0 5 1\le N,M,T \le 10^5 1N,M,T105,显然这样近似 O ( N 2 ) O(N^2) O(N2) 的算法是无法通过最大数据的,但是题目数据好像有点水

这里给出一种时间复杂度上界在于排序的 O ( M ⋅ log ⁡ 2 M + N ) O(M\cdot \log_{2}{M} + N) O(Mlog2M+N) 的算法。

考虑时刻 t t t 可能存在新增加入优先缓存的商店是哪些,只有那些在时刻 t t t 存在订单的商店,那么我们记录一下每个商店的更新时间和优先级,按时间顺序枚举订单,更新这些商店的信息,这样就能 O ( M ) O(M) O(M) 时间内维护订单造成的影响,最后再把所有商店按最终时间 T T T 更新一遍,将不符合的清除出优先缓存。

具体实现见代码,有详细的注释,并给出一种写代码时遇到问题的样例。

#include 
using namespace std;

const int N = 1e5 + 10;

struct node{
    int last, val; // i号商店上次更新的时间,以及优先级
}s[N];

struct order{
    int ti, id;
    bool operator < (const order &A)const{
        return ti < A.ti;
    }
}o[N];

int vis[N]; // 记录是否在优先缓存中
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n, m, t;
    cin >> n >> m >> t;

    for(int i = 1; i <= m; i ++){
        cin >> o[i].ti >> o[i].id;
    }

    sort(o + 1, o + 1 + m); // 先按订单顺序排序
    int ans = 0;
    for(int i = 1; i <= m; i ++){// 每次只更新有订单的商店
        auto [ti, id] = o[i];
        int time = max(0, ti - s[id].last - 1); // 计算该期间应该减少的优先级, -1是因为当前时刻有订单进来了优先级就不用减少, max{0}防止连续订单造成time负数的情况
        s[id].val = max(0, s[id].val - time); // 保证优先级不会小于0,并加上当前有订单的贡献

        // 要在加上本次的2之前先进行移除的判断,否则可能存在之前进入缓存,但因为时间流逝<=3要清除,但先加上本次的2可能使得>3and<=5 不满足又一次进入优先缓存但也没有清除
        // 详情可以参考文末提供的样例
        if(vis[id] == 1 && s[id].val <= 3){ 
            vis[id] = 0;
            ans --;
        }
        s[id].val += 2;
        if(vis[id] == 0 && s[id].val > 5){ // 如果之前不在优先缓存中,则记录
            vis[id] = 1;
            ans ++;
        }
        s[id].last = ti; // 更新 上次更新的时间(即为当前时刻)
    }
    for(int i = 1; i <= n; i ++){ // 最后将所有商店按最终时间t更新一次
        int time = t - s[i].last;
        if(vis[i] == 1 && s[i].val - time <= 3) ans --; // 若最后更新前在优先缓存中,更新后小于等于3说明需要从优先缓存中移除
    }
    cout << ans;
    return 0;
}
/*
1 4 5
1 1
1 1
1 1 // 此时1号优先级为6 加入优先缓存
…… 时间流逝后1 <= 3 移除
5 1 // 优先级为5没能再一次加入优先缓存

ans = 0
*/

你可能感兴趣的:(蓝桥杯,算法)