回旋加速器

发布时间 2023-09-26 15:33:08作者: wmtl_lofty

前言:

追着单调队列来的,写完发现题解的其他技巧蚌埠住了。

正题:

题目传送门

我们可以先对每个加速腔处理,将 $ e_i - d_i $(以下称为 $ d_i $)作为从 $ i $ 到 $ i+1 $ 的成本。因为我是单调队列的做法,显然难以处理环,于是可以断环成链,再做观察。

题目要求绕环一周,断成链后即为从 $ i $ 出发到达 $ i+n $。即要求对于 $ \forall k \in [i,i+n-1] $ 满足:

\[\sum ^{k}_ {j=i}d_j \ge 0 \]

因此我们可以先处理成前缀和。设:

\[s_i = \sum ^{i} _ {j=1} d_i \]

这样,条件就变成了对于 $ \forall k \in [i,i+n-1] $ 满足:

\[s_k - s_{i-1} \ge 0 \]

然后发现,只要尝试 $ [i,i+n-1] $ 中 $ s_{min} $ 是否满足条件,就可以得出答案。于是就可以用单调队列维护最小值啦!至于要求编号最小的,可以从 $ 1 $ 递推至 $ n $,第一个满足条件的即为编号最小的。若递推至 $ n $ 之后仍然不满足条件,即为无解,输出Failed即可。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define TP template<typename T>
#define TP_ template<typename T,typename ... T_>
TP void read(T &x)
{
	x=0;int f=1;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	x*=f;
}
TP_ void read(T &x,T_&...y){read(x);read(y...);}
TP void write(T x){if(x<0){putchar('-'),x=-x;}if(x>9)write(x/10);putchar(48+x%10);}
TP void writeln(T &x){write(x);puts("");}
TP void writesp(T &x){write(x);putchar(' ');}
TP_ void writeln(const T &x,T_ &...y){writesp(x);writeln(y...);}
typedef long long LL;
constexpr int N=1e6+5;
constexpr int inf=1e9;
LL s[N<<1];
LL d[N<<1];
int l,r,q[N<<1];
int main()
{
	int T;read(T);
	while(T--)
	{
		int n;read(n);
		for(int i=1,x;i<=n;i++)
			read(x),d[i]=x;
		for(int i=1,x;i<=n;i++)
			read(x),d[i]-=x,d[i+n]=d[i];
		for(int i=1;i<=n*2;i++)//先处理为前缀和
			s[i]=s[i-1]+d[i];
		l=1;r=0;q[l]=0;
		for(int i=1;i<=n;i++)//将1~n的范围提前处理
		{
			while(l<=r&&s[q[r]]>=s[i])r--;
			q[++r]=i;
		}
		bool success=0;
		for(int i=1;i<=n;i++)
		{
			while(l<=r&&q[l]<i)l++;//使队列中始终只有i~i+n-1的范围
			if(s[q[l]]>=s[i-1])
			{
				writeln(i);//第一次满足条件即为最小的编号
				success=1;//记录是否有答案
				break;//直接跳出
			}
			while(l<=r&&s[q[r]]>=s[i+n])r--;
			q[++r]=i+n;
		}
		if(!success)puts("Failed!");//没答案就输出Falied
	}
	return 0;
}