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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}