传送门:旅行问题
题意;首先破环成链使用一个长度为2*n的链来做,问题转化成求i以及i+n之间的最小前缀和是否大于零。
顺时针情况下
考虑第i个点时需要计算i到i+n这一端长度为n的区间里面的最小前缀和s[j],(i<=j<=i+n),
如果s[j]-s[i-1]的值小于零的话说明以i为起点不可能环游一周。
代码实现上因为j是大于i的,所以求的是最小的前缀和的值并且长度为n的区间所以要队首元素必须大于等于i+n的;
逆时针情况下
因为使用的还是前缀和数组而不是后缀和,所以s[i]用到o[i],d[i-1],所以d[0]置为d[n].
考虑第i个点时需要计算i到i-n这一段长度为n的区间里面的最大前缀和s[j],(i-n<=j<=i),
如果s[i]-s[j-1]的值小于零的话说明以i为起点不可能逆时针环游一周。
代码:
#include<iostream>
#include<cstring>
#include <cmath>
#include<algorithm>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
const int N=2e6+10;
int n,m,ans,u;
int o[N],d[N];
LL s[N];
bool st[N];
int q[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&o[i],&d[i]);
s[i]=s[i+n]=o[i]-d[i];
}
for(int i=1;i<=2*n;i++)
s[i]+=s[i-1];
int hh=0,tt=0;
q[0]=2*n+1;
for(int i=2*n;i>=0;i--)
{
if(q[hh]>i+n) hh++;
if(i<n)
{
if(s[i]<=s[q[hh]]) st[i+1]=true;
}
while(hh<=tt&&s[q[tt]]>=s[i]) tt--;
q[++tt] =i;
}
d[0]=d[n];
for(int i=1;i<=n;i++) s[i]=s[i+n]=o[i] -d[i-1];
for(int i=1;i<=2*n;i++) s[i]+=s[i-1];
hh=0,tt=0;
q[0]=0;
for(int i=1;i<=2*n;i++)
{
if(q[hh]<i-n) hh++;
if(i>n)
{
if(s[i]>=s[q[hh]]) st[i-n]=true;
}
while(hh<=tt&&s[q[tt]]<=s[i]) tt--;
q[++tt]=i;
}
for(int i=1;i<=n;i++)
if(st[i])puts("TAK");
else puts("NIE");
return 0;
}