描述

John打算驾驶一辆汽车周游一个环形公路。公路上总共有n车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。John必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。在一开始的时候,汽车内油量为零,John每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。

任务:判断以每个车站为起点能否按条件成功周游一周。

输入

第一行是一个整数n,表示环形公路上的车站数;

接下来n行,每行两个整数pi,di分别表示表示第i号车站的存油量和第i号车站到下一站的距离。

输出

输出共n行,如果从第i号车站出发,一直按顺时针(或逆时针)方向行驶,能够成功周游一圈,则在第i行输出TAK,否则输出NIE。

样例输入

5
3 1
1 2
5 2
0 1
5 4
样例输出
TAK
NIE
TAK
NIE
TAK

提示

对于全部数据,3n1e6,0pi2e9,0<di2e9

题解

  先考虑暴力怎么做:首先拆环为链。对于每个车站,我们设a[i]=p[i]-d[i],处理出a[i]的前缀和,问题就变成了枚举起点i,判断在经过的车站中是否存在一个车站j,使得sum[j]-sum[i]<0,如果是,那么就不合法。容易发现这个算法的时间复杂度是O(n)的。

  考虑优化:我们将上面的式子移项:sum[j]<sum[i],于是问题就转化为了判断对于每一个起点i,其经过车站中sum[j]的最小值是否小于sum[i]。因此我们可以用一个单调队列来维护一个前缀和的最小值,走到队首对应的终点时弹出队首并记为合法,弹出队尾时记为不合法,时间复杂度降低到了O(n)。

  放上代码:

#include<bits/stdc++.h>
using namespace std;
#define N 2000010
#define LL long long
int n,l,r,a[N],b[N],c[N],ans[N][2];
LL sum[N][2];
struct node{
    int pos;
    LL val;
}q[N];
int main(){
    scanf("%d",&n);int m=n<<1;
    for(int i=1;i<=n;i++){scanf("%d%d",&a[i],&b[i]);a[i+n]=a[i];b[i+n]=b[i];c[i]=c[i+n]=b[i-1];}
    c[1]=c[n+1]=b[n];
    for(int i=1;i<=m;i++) sum[i][0]=sum[i-1][0]+a[i]-b[i];
    for(int i=m;i>=1;i--) sum[i][1]=sum[i+1][1]+a[i]-c[i];
    l=r=1;q[l]=(node){0,0};
    for(int i=1;i<=m;i++){
        while(l<=r&&q[l].pos+n+1<=i){ans[q[l++].pos+1][0]=0;}
        while(l<=r&&q[r].val> sum[i][0]){ans[q[r--].pos+1][0]=1;}
        q[++r]=(node){i,sum[i][0]};
    }
    l=r=1;q[r]=(node){m+1,0};
    for(int i=m;i>=1;i--){
        while(l<=r&&q[l].pos-n-1>=i){ans[q[l++].pos-1][1]=0;}
        while(l<=r&&q[r].val> sum[i][1]){ans[q[r--].pos-1][1]=1;}
        q[++r]=(node){i,sum[i][1]};
    }
    for(int i=1;i<=n;i++){
        if(ans[i][0]&ans[i+n][1]) puts("NIE");
        else puts("TAK");
    }
    return 0;
}
 
01-01 06:52