vijos1091(单调队列)

来源:互联网 发布:cf有刷枪软件吗 编辑:程序博客网 时间:2024/04/30 22:58

对于环我们可以把它复制粘在后面。
然后我们对于每一个加油站,设置一个a[]表示在这一个加油站加的油减去从这个加油站开到另一个加油站的差。
那么我们要想选定某个加油站i作为起点,问题就变成了从i到i+n能够中间不断油,不断油的表现就是对于路径上的任何一个
加油站k,有a[i]+a[i+1]...+a[k]>0,我们维护一个前缀和s[i]=a[1]+a[2]+a[3]+...a[i],那么a[i]+a[i+1]+..+a[k]=
s[k]-s[i-1],我们枚举加油点i作为起点,那么我们的问题就是检验i<k<=i+n,都有s[k]-s[i-1]>0,这个问题只要我们检验s[k]中的最小值就可以解决了。那么s[k]的最小值可以用什么迅速求呢?单调队列!它的均摊复杂度就是O(1)的...然后
我们直接枚举1~n-1的每一个加油站作为起点,然后从它的区间里面用单调队列找出最小值,判断它们的差是否大于0即可。


#include<cstdio>#include<algorithm>using namespace std;const int maxn=10000009;int s[maxn],d[1000009],ss[1000009],q[1000009],n,l,a[1000009],dd[maxn],head=1,tail=1;int b[maxn]={false};void in(int i){while (q[tail]>=ss[i]) tail--;q[++tail]=ss[i];dd[tail]=i;}void out(int i){if (dd[head]<=i) head++;q[head-1]=-0x3f3f3f3f;}int main(){scanf("%d%d",&n,&l);int k=0;for (int i=1;i<=n;i++){int x,y;scanf("%d%d",&x,&y);k+=x;if (i>1) d[i-1]=d[i-1+n]=x;s[i]=s[i+n]=y;}q[0]=-0x3f3f3f3f;k=l-k;d[n]=k;for (int i=1;i<=n;i++) a[i]=a[i+n]=s[i]-d[i];for (int i=1;i<n*2;i++) ss[i]+=ss[i-1]+a[i];for (int i=1;i<=n;i++) in(i);if (q[head]>=0) b[1]=true;for (int i=2;i<=n;i++) {out(i-1);in(i+n-1);if (q[head]-ss[i-1]>=0) b[i]=true;}int ans=0;for (int i=1;i<=n;i++) if (b[i]) printf("%d ",i),ans=1;if (ans==0) printf("-1"); return 0;}




0 0
原创粉丝点击