传送门:AcWing 1241. 外卖店优先级 / 蓝桥杯2019年第十届真题

思路

将相同时刻的订单汇总到一起,然后遍历每个时刻。

只考虑有订单的时刻,对于时刻$i$,先汇总相同店的订单数,然后遍历所有订单,每个订单包含店的编号信息(假设店编号为$id$),维护店$id$上次出现订单的时刻(假设为$last[id]$),那么店$id$之前有 $i-last[id]-1$ 的时间都没有订单,此时优先级需要先减去这段没有订单的时间,若优先级<=3,则将店$id$移出缓存队列;然后优先级加上$num$个订单带来的优先级$2*num$,若优先级>5,则进入缓存队列。

最后还需要遍历所有$T$时刻无订单的店,它们最后一次出现订单的时刻到$T$时刻这段时间就是其优先级的减小值,同时更新是否被移出缓存队列。

$T$个时刻,$m$个订单,$n$个店,时间复杂度大约是$O(T+m+n)$,因为对于小于$T$的时刻并没有更新所有店的优先级,而只是更新了有订单的店的优先级,只需要在最后的$T$时刻更新一次所有店的优先级即可。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,T,last[N],sum[N];
bool f[N]; // 是否在缓存队列中
vector<int>ans[N];
unordered_map<int,int>vis;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m>>T;
    int t,id;
    for(int i=1;i<=m;i++)
    {
        cin>>t>>id;
        ans[t].push_back(id); // t时刻编号为id的店有订单
    }
    memset(last,-1,sizeof(last));
    for(int i=1;i<=T;i++) // 遍历时间
    {
        if(ans[i].size()) // i时刻有订单
        {
            int sz=ans[i].size();
            vis.clear();
            for(int j=0;j<sz;j++)
            {
                int id=ans[i][j];
                vis[id]++; // 汇总相同店的订单数
            }
            for(auto it:vis)
            {
                int id=it.first; // 店id
                int num=it.second; // 订单数num
                if(last[id]==-1) // 店id第1次出现
                {
                    sum[id]=num*2;
                    last[id]=i;
                    if(sum[id]>5)f[id]=1; // 放入缓存队列
                }
                else
                {
                    int d=i-last[id]-1;
                    sum[id]=max(0,sum[id]-d);
                    if(sum[id]<=3)f[id]=0; // 移出缓存队列
                    sum[id]+=num*2;
                    if(sum[id]>5)f[id]=1; // 放入缓存队列
                    last[id]=i;
                }
            }
        }
        if(i==T) // 处理T时刻无订单的店
        {
            for(int j=1;j<=n;j++) 
            {
                if(last[j]!=T) // 店j的最后一次订单出现在T时刻之前
                {
                    int d=T-last[j]; 
                    sum[j]=max(0,sum[j]-d);
                   if(sum[j]<=3)f[j]=0; // 移出缓存队列
                }
            }
        }
    }
    int cnt=0;
    for(int i=1;i<=n;i++)
        if(f[i])cnt++;
    printf("%d\n",cnt);
    return 0;
}
/*
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
ans:1
2 6 8
1 1
5 2
3 1
6 2
2 1
6 2
ans:1
2 6 9
1 1
5 2
3 1
6 2
2 1
6 2
ans:0
1 3 5
1 1
2 1
3 1
ans:1
1 3 6
1 1
2 1
3 1
ans:0
*/