快轉到主要內容

CSES-1163 Traffic Lights

目錄


題目連結:https://cses.fi/problemset/task/1163

題意
#

題目給定一條長度為 \( x \) 的街道,初始無紅綠燈。
接著依序在不同位置新增 \( n \) 個紅綠燈,每次新增會切斷原有路段。
我們需要每次新增紅綠燈後,求出當下最長的無紅綠燈連續路段長度。

思路
#

這是一個典型的動態區間問題。
需在序列中快速插入新節點,並更新區間長度的最大值。
在 C++ 中,可以搭配兩種資料結構實作:

  1. set:記錄所有紅綠燈位置(包含起點 \( 0 \) 與終點 \( x \))。
  2. multiset:存放所有路段長度(允許長度重複)。

新增紅綠燈位置 \( k \) 時,透過 upper_bound 找出其前後相鄰的紅綠燈。
新紅綠燈會切斷原有區間,因此需要從長度 multiset 中刪除舊的區間長度,
並加入切分後的兩段新長度。
更新完成後,最長路段即為 multiset 的最大元素(尾端)。

程式碼
#

#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define fi first
#define se second
#define INF LONG_LONG_MAX/1000
#define WA() cin.tie(0)->sync_with_stdio(0)
#define all(x) (x).begin(), (x).end()
#define int long long
#define PII pair<int, int>

signed main() { WA();
    int x, n;  cin >> x >> n;
    
    // st 儲存紅綠燈位置,dis 儲存路段長度
    set<int> st; multiset<int> dis; 
    
    // 初始化,前後各放一個紅綠燈,初始路段為整條街長
    dis.insert(x); st.insert(0); st.insert(x);
    for (; n--;) {
        int k; cin >> k;
        // 在紅綠燈位置中,找到第一個大於 k 的位置(右側的紅綠燈)
        auto it = st.upper_bound(k);
        
        // 取出這段路的長度,並從 dis 裡刪除單個元素
        dis.erase(dis.find(*it-*(prev(it))));
        
        // 將被切斷的兩段新路段長度加入 dis 中
        dis.insert(k-*prev(it));
        dis.insert(*it-k);
        
        // 將新的紅綠燈位置加入 st 裡面
        st.insert(k);
        
        // 最長路段位於 dis 尾端
        cout << *prev(dis.end()) << '\n';
    }
}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中