CodeForces - 1237D Balanced Playlist 单调队列
题意
(n)首歌循环播放。每首歌都有欢乐值。
播到某首歌时,如果这首歌的欢乐值小于当前播放过的歌曲的最大值的一半(不四舍五入),则停止。
对于每首歌,求出这首歌开始能播放多少首歌曲。
[2leq n leq 10^5
\
1leq 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]);
}