藍橋杯-日誌統計

小程xy發表於2024-05-22

小明維護著一個程式設計師論壇。現在他收集了一份”點贊”日誌,日誌共有 N 行。

其中每一行的格式是:

ts id 表示在 ts 時刻編號 id 的帖子收到一個”贊”。

現在小明想統計有哪些帖子曾經是”熱帖”。

如果一個帖子曾在任意一個長度為 D 的時間段內收到不少於 K 個贊,小明就認為這個帖子曾是”熱帖”。

具體來說,如果存在某個時刻 T 滿足該帖在 [T,T+D) 這段時間內(注意是左閉右開區間)收到不少於 K 個贊,該帖就曾是”熱帖”。

給定日誌,請你幫助小明統計出所有曾是”熱帖”的帖子編號。

輸入格式

第一行包含三個整數 N,D,K。

以下 N 行每行一條日誌,包含兩個整數 ts 和 id。

輸出格式

按從小到大的順序輸出熱帖 id。

每個 id 佔一行。

資料範圍

1≤K≤N≤1e5,
0≤ts,id≤1e5,
1≤D≤10000

輸入樣例:

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

輸出樣例:

1
3

題解:

這題正常暴力會超時

暴力程式碼👇(只能過不到一半資料)

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
const int N = 1e5 + 10;
int cnt[N];  // 統計帖子在某個時間中出現的次數
bool st[N];  // 帖子是否是熱帖
vector<pair<int,int>> v;
int main()
{
	int n, d, k; cin >> n >> d >> k;
	v.resize(n);
	
	for (int i = 0; i < n; i ++)
		cin >> v[i].x >> v[i].y;
	
	sort(v.begin(), v.end());
	
	for (int i = 0; i < n; i ++)
	{
		memset(cnt, 0, sizeof cnt);
		for (int j = i; v[j].x < v[i].x + d; j ++)
		{
			cnt[v[j].y] ++;
			if (cnt[v[j].y] >= k) 
				st[v[j].y] = true;
		}	
	}
	
	for (int i = 0; i < N; i ++)
		if (st[i]) cout << i << endl;
	return 0;
}

最優解:

  1. 先對輸入的 st 和 id 進行排序
  2. 維護一個滑動視窗, 視窗的時間長度是 d, 每次當視窗往下移動的時候, 減去前面出現時間過早的帖子id, 加上後面進來的一個帖子id。 ( 每次只進來一個帖子, 但可能出去很多個帖子, 因為需要讓整個視窗中的帖子的時間差在 區間[剛進來的帖子的st, 剛進來的帖子的st - (d - 1)] )
  3. 判斷帖子是否是熱帖

題中要求的時間區間是[t, t + d), 我們程式碼中維護的區間是[t, t + d - 1]
ac程式碼👇

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
const int N = 1e5 + 10;
int cnt[N];  // 帖子出現的次數
bool st[N];  // 是否是熱帖
vector<pair<int,int>> v;
int main()
{
	int n, d, k; cin >> n >> d >> k;
	v.resize(n);
	
	for (int i = 0; i < n; i ++) cin >> v[i].x >> v[i].y;
	
	sort(v.begin(), v.end());
	
	for (int i = 0, j = 0; i < n; i ++)
	{
	    int id = v[i].y;
		cnt[id] ++;
		
        // 不理解為什麼用的while 而不是 if的話, 看程式碼下面的圖片
		while (v[i].x - v[j].x >= d) // 使時間區間滿足 [t, t + d - 1]
		{
			cnt[v[j].y] --;
			j ++;
		}
		
		if (cnt[id] >= k) st[id] = true;
	}

	for (int i = 0; i < N; i ++)
		if (st[i]) cout << i << endl;
	return 0;
}

當 d = 10, t = 25的時候, 此時下標 j 應該在18的下標的前面, 因為 25 - 18 = 7 (小於d), 當 i ++ 後, t = 30, 此時 j 應該是21對應的下標
下標i加一, j 加的可能不只是1

覺得寫的不錯的話, 點個贊吧~