1373: [蓝桥杯2018初赛]日志统计 <—做题链接
1373: [蓝桥杯2018初赛]日志统计
时间限制: 1 Sec 内存限制: 256 MB
提交: 249 解决: 73
[状态] [提交] [命题人:外部导入]
题目描述
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。
其中每一行的格式是:ts id。表示在ts时刻编号id的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。
如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
输入
第一行包含三个整数N、D和K。
以下N行每行一条日志,包含两个整数ts和id。
1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000
输出
按从小到大的顺序输出热帖id。每个id一行。
样例输入 Copy
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
样例输出 Copy
1
3
经过仔细读题,我们发现这一题应当用双百算法来做
首先给出代码,代码里有解释
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second
const int N = 100010;
int n, d, k;
PII logs[N];
bool st[N];//用来标记id号,因为id <= 1e5,所以可以利用遍历来输出。
int cnt[N];//用来记录一个id号获得的赞数,表示形式为cnt[id]++;
int main(){
scanf("%d%d%d", &n, &d, &k);
for (int i = 0; i < n; i ++ ) scanf("%d%d", &logs[i].x, &logs[i].y);
//排序时默认以first为准排序
sort(logs, logs + n);
//双指针算法, i走在前面,j走在后面
for (int i = 0, j = 0; i < n; i ++ ){
int t = logs[i].y;//t表示为i时刻的id号
cnt[t] ++;//在j时刻,id号为t的大佬日记获得了一个赞,给t大佬加一分
//注意这里问什么是大于等于d,因为条件里给的时间段是:[T,T+D)
while (logs[i].x - logs[j].x >= d){//两个指针跨越的时间超过了d,早期的赞过期了
cnt[logs[j].y] --;//就是这位大佬,获赞的时间太久远了,赞作废了,哭去吧
j ++;//在logs[j].x时刻的太久远了,往前挪挪。
//这个循环,直到最后一个赞不过期为止。
}
//记录热帖的id号,好知道谁才是大佬
if (cnt[t] >= k) st[t] = true;
}
//遍历一遍id号,展现大佬
for (int i = 0; i <= 100000; i ++ ) if (st[i]) cout << i << endl;
return 0;
}
接着我们在说一下这题的解题思路。
根据题目意思,我们要求的是所有在时间段小于d内,所有点赞数大于或等于k的id号
所以我们要做的就是在一个区间[T, T+D)内是否有一篇热文的点赞数是否已经超过了k,并且将这个区间,从头到尾检查一遍。
而我们要解决的问题有:
1.怎么标记所有的数据——————用pair<int,int>
2.怎么标记某位id的获赞篇数————用cnt标记
3.怎么标记符合条件的id号————用bool类型数组,st[]
还有一个问题,我们怎么将这个区间块从前向后挪,依次来检验所有的热吻
这就需要使用二分法了,利用两个指针,使指针指向的区间始终使小于d的。