1. 程式人生 > 實用技巧 >CodeForces - 1237D Balanced Playlist 單調佇列

CodeForces - 1237D Balanced Playlist 單調佇列

CodeForces - 1237D Balanced Playlist 單調佇列

題意

\(n\)首歌迴圈播放。每首歌都有歡樂值。

播到某首歌時,如果這首歌的歡樂值小於當前播放過的歌曲的最大值的一半(不四捨五入),則停止。

對於每首歌,求出這首歌開始能播放多少首歌曲。

\[2\leq n \leq 10^5 \\ 1\leq a_i \leq 10^9 \]

如果這首歌會播放無窮次,輸出\(-1\)

分析

顯然,輸出-1的充要條件是當歡樂值最小的歌曲的兩倍小於歡樂值最大的歌曲

剩下的都是可行解。

很容易想到此題會涉及到連續的區間的最值。有點類似移動視窗問題。

考慮到如果維護出一個單調遞減的佇列,如果讀入的歡樂值的兩倍比隊首的歡樂值還小,顯然這個時候隊首就會在這裡結束播放,彈出。

注意到這個此題就做完了。

注意的細節

  • 不能真的去算除以2,這樣一想還不如用乘去代替
  • 陣列要開三倍空間。因為首先是迴圈播放,其次是會因為有可能要跑一圈才能遍歷所有情況。
  • 記錄答案陣列的時候有可能一些陣列不會被更新到,這個時候只需要遞推一遍即可。

程式碼

int a[maxn * 3];
int ans[maxn * 3];
int main() {
    int mx = -1;
    int mi = INF;
    int n = readint();
    for (int i = 0; i < n; i++)
        a[i] = readint(), mx = max(mx, a[i]), mi = min(mi, a[i]), a[i + n] = a[i + 2 * n] = a[i];
    if (mi * 2 >= mx) {
        for (int i = 0; i < n; i++)
            printf("-1 ");
        return 0;
    }
    deque<int> q;
    for (int i = 0; i < 3 *  n; i++) {
        while (!q.empty() && a[q.back()] < a[i]) q.pop_back();
        q.push_back(i);
        while (a[q.front()] > 2 * a[i]) {
            ans[q.front()] = i - q.front();
            q.pop_front();
        }
    }
    for (int i = 3 * n - 1; i >= 0; i--) 
        if (!ans[i]) ans[i] = ans[i + 1] + 1;
    for (int i = 0; i < n; i++)
        printf("%d ", ans[i]);
}