第九届蓝桥杯C++B组 1238. 日志统计 知识点:双指针

54 阅读2分钟

1238. 日志统计 - AcWing题库

视频解析:阿里云盘 (aliyundrive.com)

首先我们需要求出最长的时间段,然后将其划分为n个D长度的区间段,如果id在ts时刻在D区间段内 ,那么d的点赞量就加加,如果点赞量不小于k,那么我们就将其标记为热点。

最后我们遍历所有的id,看是否被标记过如果被标记过就输出

#include <bits/stdc++.h>
#define ts first
#define id second
using namespace std;

const int N = 1e5+10;
typedef pair<int,int> PII;
PII logs[N];
bool hot[N];
int cnt[N];
int n,d,k;

int main(){
    scanf("%d%d%d",&n,&d,&k);
    int maxt = 0;
    for(int i = 0;i<n;i++)
            {
                scanf("%d%d",&logs[i].ts,&logs[i].id);
                maxt = max(logs[i].ts,maxt);
            }


    //枚举n个id在 所有时刻的 点赞量
    
    for(int time = 0;time <= maxt; time++)    //从0时刻枚举到最大时刻
    {
        
    memset(cnt,0,sizeof(cnt));  //每个id的点赞量都要重置,重新计数
    
    for(int i = 1;i<=n;i++) //n个id
    {
        int t = logs[i].ts;
        int id = logs[i].id;
        
        
        //只有从[time,time+d)时间段内 id收到的点赞量才会1被考虑  因time从0开始,所以右边是开区间
        if( t>=time && t <time+d)   //如果是在d时刻内 id 点赞量 大于k 就是热帖
            cnt[id]++;
        
        //每次加完就判断一下点赞量是否不小于K 
        if(cnt[id] >= k)
            hot[id] = true;
    }
    }
    
    
    //对于每个id,如果是热帖就输出编号
    for(int id = 0;id<=100000;id++)  //id最大个数1e5
    if(hot[id]) printf("%d\n",id);

}


两层循环,n最大1e5,中间还划分了D区间,D的最大范围是1e4,因此最少也是1e9,时间复杂度就是O(n+D) image.png

优化

image.png

比如当d是2 的时候,那么[0,2]和[1,3]时间段都是大于d的时间段,但是中间重复的部分:

image.png

因此我们可以对重复部分·不用再计算,这样的话就可以只遍历一次,时间复杂度是O(n): image.png

code

在用双指针算法的时候我们需要排个序,保证其3具有单调性

#include <bits/stdc++.h>
#define ts first
#define id second
using namespace std;

const int N = 1e5 + 10;
typedef pair<int, int> PII;
PII logs[N];
bool hot[N];
int cnt[N];
int n, d, k;

int main() {
    scanf("%d%d%d", &n, &d, &k);
    int maxt = 0;
    for (int i = 0; i < n; i++)
    {
        scanf("%d%d", &logs[i].ts, &logs[i].id);
    }


    sort(logs,logs+n);
    
    
    for(int i=0,j=0;i<n;i++)
    {
        int t=logs[i].ts;
        int id=logs[i].id;
        cnt[id]++;
        while(logs[i].ts-logs[j].ts>=d)
        {
            cnt[logs[j].id]--;  //去掉开头
            j++; //加上结尾
        }
        
        if(cnt[id]>=k)hot[id]=true;
    }

    //对于每个id,如果是热帖就输出编号
    for (int i = 0; i <= 100000; i++)  //id最大个数1e5
        if (hot[i]) printf("%d\n", i);


    return 0;
}