小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有 N N N 行。其中每一行的格式是 ts id
,表示在 t s ts ts 时刻编号 i d id id 的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为 D D D 的时间段内收到不少于 K K K 个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻 T T T 满足该帖在 [ T , T + D ) [T,T+D) [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K K K 个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
分析
可以先将每个帖子的获赞信息分别存起来,对于每个帖子用单调队列在时间数组上维护一个大小为 d + 1 d+1 d+1 的窗口^1^,每次检查队列的大小是否大于 k k k 即可。
注意需要给每个帖子的获赞信息按时间排序。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, d, k, maxnt = -1e9, maxn = -1e9;
int head, tail, q[N * 10];
vector <int> nbr[N];
bool check(int num){
for(int x : nbr[num]){
q[++ tail] = x;
while(head <= tail && q[tail] - q[head] + 1 >= d + 1){
head ++;
}
if(tail - head + 1 >= k){
return true;
}
}
return false;
}
int main(){
cin >> n >> d >> k;
for(int i = 1, ts, id; i <= n; i ++){
cin >> ts >> id;
maxn = max(maxn, id);
maxnt = max(maxnt, ts);
nbr[id].push_back(ts);
}
for(int i = 1; i <= maxn; i ++){
sort(nbr[i].begin(), nbr[i].end());
}
for(int i = 0; i <= maxn; i ++){
head = 1, tail = 0;
if(check(i)){
cout << i << "\n";
}
}
return 0;
}
- 这里窗口的概念跟单调队列模板滑动窗口中的概念一致 ↩︎