洛谷P8661 [蓝桥杯 2018 省 B] 日志统计 C语言/C++

[蓝桥杯 2018 省 B] 日志统计

题目描述

小明维护着一个程序员论坛。现在他收集了一份“点赞”日志,日志共有 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 个赞,该帖就曾是“热帖”。

给定日志,请你帮助小明统计出所有曾是“热帖”的帖子编号。

输入格式

第一行包含三个整数 N N N D D D K K K

以下 N N N 行每行一条日志,包含两个整数 t s ts ts i d id id

输出格式

按从小到大的顺序输出热帖 i d id id。每个 i d id id 一行。

样例 #1

样例输入 #1

7 10 2  
0 1  
0 10    
10 10  
10 1  
9 1
100 3  
100 3

样例输出 #1

1  
3

提示

对于 50 % 50\% 50% 的数据, 1 ≤ K ≤ N ≤ 1000 1 \le K \le N \le 1000 1KN1000

对于 100 % 100\% 100% 的数据, 1 ≤ K ≤ N ≤ 1 0 5 1 \le K \le N \le 10^5 1KN105 0 ≤ i d , t s ≤ 1 0 5 0 \le id, ts \le 10^5 0id,ts105

时限 1 秒, 256M。蓝桥杯 2018 年第九届省赛

所需变量

int N;//代表赞的日志条数

int D;//代表时间段的长度

int K;//代表时间段中出现的次数

int arri[100005] = {0};//用于存储下标时间出现的次数

int arrId[100005][2] = {0};//用于存储某个序号是否是热贴

int arrTs[100005][100];//代表某时刻目前出现的序号

int i,j;//循环变量

int ts;//代表出现的时间

int id;//代表该事件出现的日志序号

int max = 0;//代表出现的时间最大值

思路:首先我们要将所有日志都按照时间轴来分开!
在每个时间上,我们将出现的点赞都记录在该时间出现的事件,然后在用D长度的时间去遍历,在这个时间段中那些日贴序号出现了多少次,如果大于我们目前的所定义的K,我们就认为是热帖!分析结束我们就开始编写代码!
首先我们将输入进来的每个时间和序号都存入以时间为轴记录的事件数组中,代码如下:

for(i = 0;i<N;i++){
    cin>>ts>>id;
    arrTs[ts][arri[ts]++] = id;
    if(ts>max){
        max = ts;
    }
}

得到以时间为区分的轴之后,我们第一步做的就是将从时间0开始到D这个时间段的每个序号所受到的点赞都累加起来!并且每次加进去我们都判断这个序号的日志是否满足K,日过满足直接就标记一下他是热帖!代码如下:

for(i = 0;i<D;i++){
    for(j = 0;j<arri[i];j++){
       if(arrId[arrTs[i][j]][0] == 1){
           continue;
       }
        arrId[arrTs[i][j]][1]++;
        if(arrId[arrTs[i][j]][1]>=K){
            arrId[arrTs[i][j]][0] = 1;
        }
    }
}

**得到第一个时间段后,我们的做法就是不断往后遍历,这里有个小技巧,我们不需要每次都全部加起来!!!
因为第二个时间段就是[1,D+1),

本人的做法是在上次的基础上将0时间的日志记录删掉,然后将D时间的点赞日志加起来,这样我们就做到时间复杂度为O(n)

,如果按照正常思路每次重新累加,时间复杂度为O(N*D) ,大大降低运行时间,具体代码如下**

for(i = 1;i<max-D+1;i++){
    for(j = 0;j<arri[i];j++){
        arrId[arrTs[i][arri[i]]][1]--;
    }
    for(j = 0;j<arri[i+D];j++){
        if(arrId[arrTs[i+D][j]][0] == 1){
           continue;
       }
        arrId[arrTs[i+D][j]][1]++;
        if(arrId[arrTs[i+D][j]][1]>=K){
            arrId[arrTs[i+D][j]][0] = 1;
        }
    }
}

将上面都遍历结束后,我们再从开始遍历,如果标记为1,代表这个序号曾经是热帖,我们就将其输出出来!

for(i = 0;i<100004;i++){
    if(arrId[i][0] == 1){
        cout<<i<<endl;
    }
}

完整代码如下(编译器是dev,语言是C语言):

#include<iostream>
using namespace std;
int main(){
    //arrId[i][0]代表是否是热帖,[1]代表现在是多少
    int N,D,K,arri[100005] = {0},arrId[100005][2] = {0},arrTs[100005][100];
    int i,ts,id,max = 0,j;
    cin>>N>>D>>K;
    for(i = 0;i<N;i++){
        cin>>ts>>id;
        arrTs[ts][arri[ts]++] = id;
        if(ts>max){
            max = ts;
        }
    }
    for(i = 0;i<D;i++){
        for(j = 0;j<arri[i];j++){
           if(arrId[arrTs[i][j]][0] == 1){
               continue;
           }
            arrId[arrTs[i][j]][1]++;
            if(arrId[arrTs[i][j]][1]>=K){
                arrId[arrTs[i][j]][0] = 1;
            }
        }
    }
    for(i = 1;i<max-D+1;i++){
        for(j = 0;j<arri[i];j++){
            arrId[arrTs[i][arri[i]]][1]--;
        }
        for(j = 0;j<arri[i+D];j++){
            if(arrId[arrTs[i+D][j]][0] == 1){
               continue;
           }
            arrId[arrTs[i+D][j]][1]++;
            if(arrId[arrTs[i+D][j]][1]>=K){
                arrId[arrTs[i+D][j]][0] = 1;
            }
        }
    }
    for(i = 0;i<100004;i++){
        if(arrId[i][0] == 1){
            cout<<i<<endl;
        }
    }
    return 0;
}

洛谷P8661 [蓝桥杯 2018 省 B] 日志统计 C语言/C++_第1张图片

你可能感兴趣的:(蓝桥杯真题,洛谷算法题,蓝桥杯,c++,c语言)