Description
现在你有N个数,分别为A1,A2,…,AN,现在有M组询问需要你回答。每个询问将会给你一个L和R(L<=R),保证Max{Ai}-Min{Ai}<=R-L,你需要找出并输出最小的K(1<=K<=N,不存在输出-1)满足以下两个条件:
①能够在原来的N个数中选出不重复(下标不重复)的K个数,使得这K个数的和在区间[L,R]内。
②能够在原来的N个数中选出不重复(下标不重复)的K个数,使得这K个数的和不在区间[L,R]内。
N,M<=100000
Input
输入第一行两个正整数N,M。
第二行N个数,表示A1,A2,…,AN
接下来M行表示M组询问,每组询问两个正整数L,R,意义如上。
Output
输出共M行,每一行输出对应询问的最小的K(不存在输出-1)。
Sample Input
5 3
3 6 5 8 7
29 34
2 8
1 10
Sample Output
-1
2
2
Solution
先把原数列排序,因为再选值的时候不需要连续
记录前缀和还有后缀和
对于输入的l和r
分别用二分找到:
x:前缀和小于l的最后一个位置
y:后缀和大于r的第一个位置
z:后缀和大于等于l的第一个位置
q:前缀和小于等于r的最后一个位置
y和z还需要用n减一下
有什么用呢?
设k为取多少个数
对于k=1~x,都可以取到k个数的和小于l,即满足条件2
对于k=n-y+1~n,都可以取到k个数的和大于r,即满足条件2
也就是说当k为1~x或y~n时都可以满足条件2
由题意“保证Max{Ai}-Min{Ai}<=R-L”得,当你现在的q个数的和还小于l时,再加一个数不可能大于r
那么当k=z~n时,都可以取到k个数的和大于l
k=1~n-q+1时,都可以取到k个数的和小于r
也就是k=z~n-q+1时,都可以满足条件2,然后求交集中的最小值就行了
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define fd(i,a,b) for(ll i=a;i>=b;i--)
#define N 101000
#define M 300
#define ll long long
using namespace std;
ll a[N],n,s[N],g[N],q,l,r;
ll find(ll x)
{
ll l=0,r=n;
while(l+1<r)
{
ll m=(l+r)/2;
if(s[m]<x) l=m;else r=m;
}
return l;
}
ll find1(ll x)
{
ll l=0,r=n;
while(l+1<r)
{
ll m=(l+r)/2;
if(g[m]>x) l=m;else r=m;
}
return l;
}
ll find2(ll x)
{
ll l=0,r=n;
while(l+1<r)
{
ll m=(l+r)/2;
if(s[m]<=x) l=m;else r=m;
}
if(s[r]<=x) l=r;
return l;
}
ll find3(ll x)
{
ll l=0,r=n;
while(l+1<r)
{
ll m=(l+r)/2;
if(g[m]>=x) l=m;else r=m;
}
if(g[r]>=x) l=r;
return l;
}
int main()
{
scanf("%lld%lld",&n,&q);
fo(i,1,n) scanf("%lld",&a[i]);
sort(a+1,a+n+1);
fo(i,1,n) s[i]=s[i-1]+a[i];
fd(i,n,1) g[i]=g[i+1]+a[i];
for(;q;q--)
{
scanf("%lld%lld",&l,&r);
ll x=find(l),y=find1(r),z=find3(l),q=find2(r);
z=n-z+1;y=n-y+1;
ll ans=-1;
if(z<=x) ans=z;
else if(q>=y) ans=y;
printf("%lld\n",ans);
}
}