BZOJ3827[Poi2014] Around the world

本文解析了BZOJ3827飞行模拟问题,介绍了一种基于动态规划的方法来确定飞机绕行赤道所需最少降落次数。通过计算前缀和并优化区间分割,实现了高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BZOJ3827[Poi2014] Around the world

Description

通过几年的努力,Byteasar最终拿到了飞行员驾驶证。为了庆祝这一事实,他打算买一架飞机并且绕Byteotia星球赤道飞行一圈。但不幸的是赤道非常长所以需要中途加几次油。现在已知赤道上面所有飞机场,所有飞机从飞机场起飞降落也可以加油。因为买飞机是个十分重大的决定,Byteasar决定寻求你的帮助。他将会让你模拟不同的飞行路线。自然这些飞机一次能走的航程是不同的。对于每次模拟,他想要知道最少需要降落多少次(包括最后一次)。需要注意的是起点可以任意选取。

Input

第一行有两个正整数n和s(2<=n<=1000000,1<=s<=100)分别代表赤道上面的飞机场个数和询问的航班数第二行有n个正整数L1,L2…Ln(L1+L2+…+Ln<=10^9)按顺序给出了n个相邻机场之间的距离。其中Li是第i个与第i+1个机场之间的距离,Ln是第n个和第1个之间的距离。第三行给出了n个正整数di(1<=di<=L1+L2+…+Ln)。其中di代表第i个飞机一次能走的航程。

Output

输出s行每行一个整数代表最小降落次数或者一个字符串“NIE”代表不能到达

Sample Input

6 4

2 2 1 3 3 1

3 2 4 11

Sample Output

4

NIE

3

2

Hint

img

Solution:

蒟蒻表示根本不会写,默默写了一个错误的贪心…

大神说这道题就是Codeforces 526 E Transmitting Levels

给定一个环,将它分割成几份,使得每一份的长度不大于 d 。这题有神奇的dp做法:

首先断环成链,计算前缀和。

我们计算每一个点作为该区间结尾端点的cnt值和 last 值, cnt 值记录 [last[i],i] 区间完成分割的分割数,这是满足无后效性原则的。初始时 cnt[i]=0,last[i]=i

当我们枚举一个点 i 时,这个区间肯定要贪心地取最多的长度,于是取:

minimizejs.t.sum[i]sum[j]d

转移:

cnt[i]=cnt[j]+1last[i]=last[j]

当出现 ilast[i]n 时,则就可以直接输出 cnt[i] 了。

#include<stdio.h>
#define M 1000005
inline void Rd(int &res){
    char c;res=0;
    while(c=getchar(),c<'0');
    do{
        res=(res<<1)+(res<<3)+(c^48);
    }while(c=getchar(),c>='0');
}
int A[M<<1],last[M<<1],cnt[M<<1];
long long sum[M<<1];
int main(){
    int n,q;
    Rd(n);Rd(q);
    for(int i=1;i<=n;i++){
        Rd(A[i]);
        A[i+n]=A[i];
    }
    for(int i=1;i<=n*2;i++)
        sum[i]=sum[i-1]+A[i],last[i]=i;
    while(q--){
        int d,j=1;
        Rd(d);
        for(int i=1;i<=n*2;i++)cnt[i]=0,last[i]=i;
        for(int i=n+1;i<=n*2;i++){
            while(sum[i]-sum[j]>d)j++;
            cnt[i]=cnt[j]+1;
            last[i]=last[j];
            if(i-last[i]>=n){
                printf("%d\n",cnt[i]);
                break;
            }
        }
    }
    return 0;
}

于是就可以用几乎一样的代码完成这道题了,复杂度 O(ns)

#include<stdio.h>
#define M 1000005
int A[M<<1],sum[M<<1],last[M<<1],cnt[M<<1];
inline void Rd(int &res){
    char c;res=0;
    while(c=getchar(),c<'0');
    do{
        res=(res<<1)+(res<<3)+(c^48);
    }while(c=getchar(),c>='0');
}
int main(){
    int n,s,mx=0;
    Rd(n);Rd(s);
    for(int i=1;i<=n;i++){
        Rd(A[i]);
        if(mx<A[i])mx=A[i];
        A[i+n]=A[i];
    }
    for(int i=1;i<=n*2;i++)
        sum[i]=sum[i-1]+A[i],last[i]=i;
    while(s--){
        int d,j=1;
        Rd(d);
        if(d<mx){
            puts("NIE");
            continue;
        }
        for(int i=1;i<=n*2;i++)last[i]=i,cnt[i]=0;
        for(int i=n+1;i<=n*2;i++){
            while(sum[i]-sum[j]>d)j++;
            last[i]=last[j];
            cnt[i]=cnt[j]+1;
            if(i-last[i]>=n){
                printf("%d\n",cnt[i]);
                break;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值