题目大意:
给出N个数,Q个询问 要求你输出Q的询问的结果,询问的内容为,找出一个区间,区间内的数的和的绝对值要最接近所给的数
解题思路:
这种求区间和的问题,比如求一个连续区间和大于
k
k
k且长度最短,求一个连续区间和小于
k
k
k且长度最长,求一个连续区间和绝对值最接近
k
k
k都很适合使用尺取法进行求解。之前写过一篇尺取法的用法,我们要解决的无非下面三个问题
1、 什么情况下能使用尺取法?
2、如何推进区间的端点?
3、如何利用当前区间更新结果值?
4、何时结束区间的枚举?
只要一次解决了这三个问题,那么代码也就呼之欲出了。令 l , r l,r l,r分别为尺取法的左右端点,我们分别来看
-
尺取法的使用需要数组满足一定的性质,比如单调性,但是这里即使我们求出前缀和 s u m [ i ] sum[i] sum[i],元素有正有负他也不是单调的。要单调必须排序,但是我们会发现排序之后,由于我们每次取的是前缀和之差的绝对值,所有排序对位置影响不大 a b s ( s u m [ i ] − s u m [ j ] ) = = a b s ( s u m [ j ] − s u m [ i ] ) abs(sum[i] - sum[j]) == abs(sum[j] - sum[i]) abs(sum[i]−sum[j])==abs(sum[j]−sum[i])。不过此时我们需要额外的存储空间,记录该前缀和的原本位置。
-
一般的思路 a b s ( s u m [ i ] − s u m [ j ] ) > Q abs(sum[i] - sum[j])>Q abs(sum[i]−sum[j])>Q时我们需要推进左端点,否则推进右端点。为了防止出现 l > = r l>=r l>=r,我们可以再加上 i f ( l = = r ) r + + if(l==r)r++ if(l==r)r++。
-
对每一组 l , r l,r l,r,如果 a b s ( s u m [ r ] − s u m [ l ] ) abs(sum[r] - sum[l]) abs(sum[r]−sum[l])的值更为接近 Q Q Q,那么就可以更新结果值以及记录此时的左右端点。
-
最简单的,当 l , r l,r l,r有一个区间端点时就结束枚举,因为如果 r r r先移到端点,说明此时 a b s ( s u m [ r ] − s u m [ l ] ) < Q abs(sum[r] - sum[l])<Q abs(sum[r]−sum[l])<Q,再右移 l l l只会使得值更小,差距更大、
#include<iostream>
#include<cmath>
#include<iomanip>
#include<vector>
#include<map>
#include<queue>
#include<algorithm>
#include<string.h>
using namespace std;
#define ll int
#define Max 100050
struct elem {
ll v, id;
}sum[Max];
bool cmp(elem e1, elem e2) {
return e1.v < e2.v;
}
ll n, k, e[Max];
int main() {
while (cin >> n >> k) {
if (n + k == 0) break;
e[0] = 0; sum[0].v = 0; sum[0].id = 0;
for (ll i = 1; i <= n; i++) { cin >> e[i]; sum[i].v = sum[i - 1].v + e[i]; sum[i].id = i; }
sort(sum , sum + n + 1, cmp);
for (ll i = 0; i < k; i++) {
ll q, l = 0, r = 1; cin >> q;
ll res = 2000000010, minl = 0, minr = 0, ans = 0;//res:当前最小的误差
while (l <= n && r <= n) {
ll val = abs(sum[r].v - sum[l].v), tmp = abs(val - q);
if (tmp < res) {
res = tmp; ans = val;
minl = sum[l].id; minr = sum[r].id;
}
if (val > q)l++;
else if (val < q)r++;
else break;
if (l == r) r++;
}
if (minl > minr) swap(minl, minr); minl += 1;
cout << ans << " " << minl << " " << minr << endl;
}
}
}