JZOJ 3943. 【GDOI2015模拟11.29】环游世界

21 篇文章 0 订阅
13 篇文章 0 订阅

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(SN)

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值