Acwing 1088.旅行问题
题目:
一个环形公路,由n个车站,每个站有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。
从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。
在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。
任务:判断以每个车站为起点能否按条件成功周游一周。
题解:
破环成链,链复制
我们从点i出发,能否到i+1,取决于i的油量是否大于等于d[i],即i到i+1的距离
所有我们规定数组w[i]=a[i]-d[i]
s[i]表示w[i]的前缀和
从i出发到j,这个过程的油量始终>=0,等价于在[i,i+n-1]中,对任意的j,i<=j<=i+n-1,均有s[j]-s[i-1]>=0,如果min(s[j]-s[i-1])>=0,就说明能到达,其中i是固定的,所有就是找s[j]的最小值。
这就把问题引到滑动窗口,这既是单调队列优化
题目中说顺时针或逆时针有一个就行,所以我们还要倒着来遍单调队列
代码:
#include<iostream>
#include<algorithm>
#include<cstring>using namespace std;const int N=1e6+10;long long s[N*2];
int q[N*2],o[N],d[N],mark[N];int main()
{int n;scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d%d",&o[i],&d[i]);for(int i=1;i<=n;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=-1;for(int i=1;i<=2*n;i++){if(hh<=tt&&q[hh]+n<i) hh++;//因为q里面应该要包括i,所以q[hh]可以等于i-n,但不能小于while(hh<=tt&&s[q[tt]]>=s[i]) tt--;q[++tt]=i;if(i>=n&&s[q[hh]]>=s[i-1-n]) mark[i-n]=1;}hh=0,tt=-1;d[0]=d[n];for(int i=1;i<=n;i++) s[i]=s[i+n]=o[i]-d[i-1];for(int i=2*n;i>=0;i--) s[i]+=s[i+1];for(int i=1;i<=2*n;i++){if(hh<=tt&&q[hh]<i-n+1) hh++;while(hh<=tt&&s[q[tt]]>=s[i]) tt--;q[++tt]=i;if(i>n&&s[q[hh]]>=s[i+1]) mark[i-n]=1;}for(int i=1;i<=n;i++)if(mark[i]) printf("TAK\n");else printf("NIE\n");return 0;
}