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
先把图复制一遍
变成一个1到2n的链
我们先以1作为起点
设一个g数组 g[x]表示飞机第x次降落最多能到达的城市
然后我们就从1开始往后不断切换起点
显然g数组只会往后移
用个指针维护一下即可
我们发现起点不需要右移到g[1]之后
因为g[1]之后的就相当于在前面跳过一次
那么这样子的复杂度就可接受了
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int N=1000005;
int n,i,j,cnt,ans,m,fl,t;
int g[N<<1],s[N<<1],a[N<<1];
int read(){
int sum=0;
char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9'){
sum=sum*10+c-'0';
c=getchar();}
return sum;
}
int main(){
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
n=read(),t=read();
fo(i,1,n){
a[i]=read();
a[i+n]=a[i];}
fo(i,2,n*2) s[i]=s[i-1]+a[i];
while (t){
t--;
m=read();
g[cnt=0]=0;
ans=123456789;
fl=0;
fo(i,1,n){
if (a[i]>m) fl=1;
if (s[i]-s[g[cnt]]>m) g[++cnt]=i-1;}
g[++cnt]=n;
if (fl) { printf("NO\n"); continue;}
int l=g[1];
fo(i,1,l){
g[0]++;
fl=0;
fo(j,1,cnt){
if (g[j]<=g[j-1]) g[j]++;
while (s[g[j]+1]-s[g[j-1]]<=m&&g[j]<i+n) g[j]++;
if (g[j]==i+n) {
fl=1;
ans=min(ans,j);
break; }
}
if (!fl) g[++cnt]=i+n;
}
printf("%d\n",ans);}
}