描述
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
3 1
1 2
5 2
0 1
5 4
样例输出
TAK
NIE
TAK
NIE
TAK
NIE
TAK
NIE
TAK
提示
对于全部数据,3≤n≤1e6,0≤pi≤2e9,0<di≤2e9
题解
先考虑暴力怎么做:首先拆环为链。对于每个车站,我们设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; }