Description
ZTY 想要环游世界!他会驾驶一架飞机沿着赤道顺时针飞行一周。现在他手头有 S 架飞
机,每架飞机的油箱都有自己的容量限制di,表示在飞机的油箱装满的情况下,最远可以飞
行多远。赤道上有 n 座城市,相邻两座城市间有一定的距离。ZTY 有很多钱,所以你可以不
用考虑加油的问题,只需要认为降落到每座城市都可以把飞机的油箱加满。
ZTY 想要尽快完成环游世界的目标,所以他想要飞机降落的次数尽量少。那么 ZTY 就想
要问你:对每架飞机,最少降落多少次才可以完成环游世界的任务?注意:ZTY 可以选择任
意一个城市作为他的起点。另外,最后回到起点也需要算一次降落。
Input
第一行两个整数,表示 n 与 s。
接下来一行 n 个整数,第 i 个整数表示第 i 个城市与第 i+1 个城市间的距离(第 n 个整数表
示第 n 个城市与第 1 个城市间的距离)。
接下来 s 个整数,第 i 个整数表示第 i 架飞机的容量限制di。
Output
对 s 架飞机每架飞机输出一行。如果 ZTY 使用这架飞机不可能完成环游世界的目标,输出
“NO”。否则输出 ZTY 最少需要降落多少次。
Sample Input
样例 1:
6 4
2 2 1 3 3 1
3 2 4 11
样例 2:
8 3
2 2 2 2 2 2 2 2
1 16 2
Sample Output
样例 1:
4
NO
3
2
样例 2:
NO
1
8
Data Constraint
对 30%的数据 n 不超过 1000。
对 50%的数据 n 不超过 100000。
对另 10%的数据 s 不超过 5。
对 100%的数据 n 不超过 1000000,s 不超过 100。赤道长度(所有距离和)不超过109。所
有输入小于231。
Hint
样例 1 解释:
对第一架飞机,可以按 6->2->4->5->6 的顺序来飞行。
对第二架飞机,显然它不能越过 4 与 5 之间 3 的距离。
Solution
用一个数组 g[i] 表示当前的最优降落点集合。
那么先以 1 为起始点,往后处理出 g[i] (贪心地取),
可以用前缀和来快速地算。
之后再更改起始点 i ,范围为
1 到 g[1] ,因为之后的话就不必要了,相当于在前面先取。暴力更新当前的 g[i] ,由于点只会右移,由单调性知其均摊复杂度为 O(1) 。
则遍历到 i+n 后查看 g[i] 中的元素个数是否优于答案即可。
总时间复杂度 O(S∗N) 。
Code
#include<cstdio>
#include<cctype>
using namespace std;
const int N=2e6+5;
int mx,tot;
int a[N],f[N],g[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline int min(int x,int y)
{
return x<y?x:y;
}
int main()
{
int n=read(),s=read();
for(int i=1;i<=n;i++)
{
a[i]=a[i+n]=read();
if(a[i]>mx) mx=a[i];
}
for(int i=1;i<=n<<1;i++) f[i]=f[i-1]+a[i];
while(s--)
{
int d=read();
if(d<mx)
{
puts("NO");
continue;
}
g[0]=tot=0;
for(int i=1;i<=n;i++)
if(f[i]-f[g[tot]]>d) g[++tot]=i-1;
g[++tot]=n;
int ans=tot;
for(int i=1,q=g[1];i<=q;i++)
{
g[0]++;
bool pd=true;
for(int j=1;j<=tot;j++)
{
if(g[j]<=g[j-1]) g[j]=g[j-1]+1;
while(g[j]<i+n && f[g[j]+1]-f[g[j-1]]<=d) g[j]++;
if(g[j]==i+n)
{
ans=min(ans,j);
pd=false;
break;
}
}
if(pd) g[++tot]=i+n;
}
printf("%d\n",ans);
}
return 0;
}