题面:
(炽焰的不死鸟啊…愿你在蓬莱的彼岸仍旧翱翔…)
妹红读完了《蓬莱人形》中有关8名正直者的部分,决定即刻去人里寻找正直者进行灭罪。
结果是,妹红成功找到了N名正直者,并把他们从1~N依次编号,正直者们的身上各自有值为a[i]的正直点数,并就地取材将他们挂在了蓬莱玉枝上形成一条链子。
妹红要在这N名正直者中挑出连续的若干名进行一次灭罪,而在挑选时,只可能出现下面两种情况:
先将选择的若干名正直者的正直点数进行求和,
i)当存在一个选择的区间,使求和的结果刚好等于一个数字M时,可以进行灭罪
ii)当任意一个选择的区间,使求和的结果都不等于一个数字M时,不可以进行灭罪
现在,妹红想请居住在人里的你帮她设计出灭罪的选择方案。
PS:注意选择的区间是连续的,不是任意子区间。
输入格式:
本题仅有一组数据
第一行给出一个正整数N和一个正整数M(1<=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)
}
}