蓬莱人形&灭罪「正直者之死」

题面:

image.png


(炽焰的不死鸟啊…愿你在蓬莱的彼岸仍旧翱翔…)
妹红读完了《蓬莱人形》中有关8名正直者的部分,决定即刻去人里寻找正直者进行灭罪。
结果是,妹红成功找到了N名正直者,并把他们从1~N依次编号,正直者们的身上各自有值为a[i]的正直点数,并就地取材将他们挂在了蓬莱玉枝上形成一条链子。
妹红要在这N名正直者中挑出连续的若干名进行一次灭罪,而在挑选时,只可能出现下面两种情况:

先将选择的若干名正直者的正直点数进行求和,
i)当存在一个选择的区间,使求和的结果刚好等于一个数字M时,可以进行灭罪
ii)当任意一个选择的区间,使求和的结果都不等于一个数字M时,不可以进行灭罪

现在,妹红想请居住在人里的你帮她设计出灭罪的选择方案。

PS:注意选择的区间是连续的,不是任意子区间。

输入格式:

本题仅有一组数据
第一行给出一个正整数N和一个正整数M1<=N<=1e5,1<=M<=1e7),含义如上述题意
第二行给出N个正整数a[i](1<=i<=N,1<=a[i]<=1e3),含义如上述题意

输出格式:

第一行首先判断妹红能不能进行灭罪
如果不可以,直接输出一个字符串"no way"(引号不输出),结束输出

如果可以,请给出可行的选择方案,格式如下,
每行输出一种选择正直者区间的方案,格式为"l-r"(引号不输出),其中l是选择的左端点编号,r是右端点编号,若方案不唯一,以l递增的顺序输出所有方案。
题目数据保证所有求和的结果均在长整型范围内。

输入样例1:

16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13

输出样例1:

1-5
4-6
7-8
11-11

样例解释1:

以闭区间[1, 5]为例,内含元素{3, 2, 1, 5, 4},求和3+2+1+5+4正好为15,是一种可行的方案,
此外的[4, 6],[7, 8],[11, 11]区间也是符合求和正好为15的方案之一,此外注意题目要求的递增输出顺序。

输入样例2:

6 100
1 1 4 5 1 4

输出样例2:

no way

样例解释2:

显然,无论怎么选择区间,都不能使求和结果等于100,不存在可行灭罪方案

解题思路:

初步读题,不难看出是考察区间求和问题。

主要的解题点其实只有两个,一是怎么去快速获得一个选择区间的和,二是怎么去查找出符合的区间

对于第一点,由于区间值已给定,之后所有的操作都是进行单一查询,可以直接使用前缀和来解决。

对于第二点,枚举爆搜来遍历一个个可能出现的区间方案,是一种方法,但考虑到题目时间空间的限制,显然不太现实,那么就需要自设计一定程度的快速查找算法,基本的可以考虑到使用二分双指针,又注意到题面给定序列是杂乱无序的,使用二分的做法会稍显繁琐,直接采用双指针查找即可。

题解代码:

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

using namespace std;
using ll=long long;

const int MAX=1e5+10;
int N;
bool ok;        //判断能不能找到合法方案的bool标记
int D[MAX], sum[MAX], M;

void Count(int, int, int);

int main(void){
    IOS;
      cin>>N>>M;
    for(int i=1;i<=N;i++){
          cin>>D[i];
        if(i==1) sum[i]=D[i];
        else sum[i]=sum[i-1]+D[i];    //创建前缀和数组sum保存区间值,sum[n]代表的就是D数组中[1,n]区间值之和
    }

    Count(0, 1, M);        //寻找合法区间函数

    if(!ok) cout<<"no way\n";

    return 0;
}



void Count(int l, int r, int cmp){    //参数从左至右,依次代表左端点,右端点,需要满足的区间求和值
    while(r<=N){                    //双指针,快指针l和慢指针r,快指针在后移时要保证始终在慢指针之前,即左端点下标不能够大于右端点下标
        while(l<r&&sum[r]-sum[l+1]>=cmp){
            l++;        //当发现左端点后移一位(当前区间的下界+1)之和,区间值还是大于等于需要满足的求和值时,可以继续移动快指针,直到找到合法的区间
        }
        
        int temp=sum[r]-sum[l];    //temp保存l~r区间和
        if(temp==cmp){            //如果满足给定值,说明是一个可行方案,更新寻找标记ok,直接输出该次方案即可
            ok=true;
              cout<<l+1<<'-'<<r<<'\n';
        }
        
        r++;        //完成这次区间寻找的操作之后慢指针后移一位(当前区间上界+1)
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值