jzoj3943 环游世界

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);}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值