Files
python/TangDou/AcWing/MonotonicQueue/1088.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

55 lines
2.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2000010; // 破环成链,双倍长度
int n, p[N], d[N]; // n:油站数量,p:加上的油量,d:消耗掉的油量
LL s[N]; // 顺时针:p[i-1]-d[i-1] 的前缀和,逆时针:p[i + 1] - d[i] 的后缀和
int q[N], hh, tt; // 队列
bool ans[N]; // 结果数组,因为每个站点都有一个结果:是不是能从它出发环行一周,所以,需要一个结果数组
int main() {
// 此题目数据量 n<=1e6,数据量大,使用scanf进行读取
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &p[i], &d[i]);
// 破环成链
p[i + n] = p[i]; // 在i点的加油量
d[i + n] = d[i]; // ① 顺时针:从i到下一站i+1的耗油量,②逆时针:从i+1到下一站i的耗油量
}
// 一、顺时针
// (1) 前缀和
for (int i = 1; i <= n * 2; i++) s[i] = s[i - 1] + p[i - 1] - d[i - 1];
// 每个节点考查它右侧最长n个长度的窗口中,s[j]的最小值=s[q[hh]]
// 它右边i+1,i+2,...,i+n需要先入队列,才能让i看到未来,倒序遍历
// (2) 哨兵
q[0] = n * 2 + 1; // 倒序遍历,添加右侧哨兵
hh = 0, tt = 0; // 单调队列
for (int i = n * 2; i; i--) { // 倒序遍历
while (hh <= tt && q[hh] - i > n) hh++; // 最长n个站点
/*
① 如果最小值都大于s[i],说明i可以环形完成旅行
② 走到i时,没加上i站点的油前,考查前i-1,i-2,..,i-n的情况,i还没有参加讨论,所以先用队列解决问题后,再将i入队列
*/
if (s[q[hh]] >= s[i]) ans[i] = true; // s[q[hh]]=s[j]区间内最小值,s[j]-s[i]>=0就是可以走到
while (hh <= tt && s[q[tt]] >= s[i]) tt--; // 准备i入队列,保留年轻+漂亮(数值更小),喜新厌旧,什么东西!
q[++tt] = i; // i入队列
}
// 二、逆时针,后缀和
for (int i = 2 * n; i; i--) s[i] = s[i + 1] + p[i + 1] - d[i]; // 这里有一个细节,d[i]其实就是i+1->i消耗掉的油量
q[0] = 0; // 正序遍历,添加左侧哨兵
hh = 0, tt = 0; // 初始化队列
for (int i = 1; i <= 2 * n; i++) { // 正序遍历
while (hh <= tt && i - q[hh] > n) hh++; // 最长n个站点
if (s[q[hh]] >= s[i]) ans[i - n] = true;
while (hh <= tt && s[q[tt]] >= s[i]) tt--;
q[++tt] = i;
}
// 输出
for (int i = 1; i <= n; i++) puts(ans[i] ? "TAK" : "NIE");
return 0;
}