题意
给定
n
n
个数,与 个
s
s
值,求区间总和的绝对值最接近 的一个区间,并输出它的总和绝对值、左端点、右端点。
1≤n≤100000
1
≤
n
≤
100000
思路
用尺取法必须要有单调性,总和的绝对值似乎找不到单调性,那么只能创造一个单调性。而直接将原数组排序,无法得到原来区间的总和。由此想到将原数组求前缀和再将前缀数组升序排序,那么任选两个数作差都对应着一个区间和的绝对值,又由于要输出区间的两个端点,所以要将前缀和与下标封成结构体排序。那么原问题就化为了一个升序区间中取两个数作差求最接近 s s 的值。特别注意的是右推时要推到最佳的右端点,而且左右端点相差至少为 。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
int a[100003];
struct node
{
int v,pos;
bool operator <(const node &_)const
{
return v<_.v;
}
}s[100003];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m),n||m)
{
FOR(i,1,n)scanf("%d",&a[i]);
s[0].v=s[0].pos=0;
FOR(i,1,n)
{
s[i].v=s[i-1].v+a[i];
s[i].pos=i;
}
sort(s,s+1+n);
FOR(i,1,m)
{
int t,R=0,ans=2e9+10,_L,_R;
scanf("%d",&t);
FOR(L,0,n)
{
while(L>=R||R<n&&abs(s[R+1].v-s[L].v-t)<=abs(s[R].v-s[L].v-t))R++;
if(R>n)break;
if(abs(s[R].v-s[L].v-t)<abs(ans-t))
{
ans=s[R].v-s[L].v;
_L=min(s[L].pos,s[R].pos)+1;
_R=max(s[L].pos,s[R].pos);
}
}
printf("%d %d %d\n",ans,_L,_R);
}
}
return 0;
}