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