发现两篇题解都使用的是暴力模拟,时间复杂度为
O
(
N
⋅
T
)
O(N\cdot T)
O(N⋅T) 的算法,题目数据范围
1
≤
N
,
M
,
T
≤
1
0
5
1\le N,M,T \le 10^5
1≤N,M,T≤105,显然这样近似
O
(
N
2
)
O(N^2)
O(N2) 的算法是无法通过最大数据的,但是题目数据好像有点水。
这里给出一种时间复杂度上界在于排序的 O ( M ⋅ log 2 M + N ) O(M\cdot \log_{2}{M} + N) O(M⋅log2M+N) 的算法。
考虑时刻 t t t 可能存在新增加入优先缓存的商店是哪些,只有那些在时刻 t t t 存在订单的商店,那么我们记录一下每个商店的更新时间和优先级,按时间顺序枚举订单,更新这些商店的信息,这样就能 O ( M ) O(M) O(M) 时间内维护订单造成的影响,最后再把所有商店按最终时间 T T T 更新一遍,将不符合的清除出优先缓存。
具体实现见代码,有详细的注释,并给出一种写代码时遇到问题的样例。
#include <bits/stdc++.h>
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
*/