You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

55 lines
2.8 KiB

2 years ago
#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
iii-1,i-2,..,i-nii
*/
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;
}