题解 P6473 【[NOI Online #2 入门组]未了】
贪心+分块
一、算法解析
1.贪心
这个贪心大家都很容易看的出来,由大到小施魔法是最容易达成目的的。证明我就不证了。
但是裸查询的耗时为
2.优化
这里用了分块来优化查询,将排好序的魔法序列分成大小为
3.分块大小
如果直接开大小 在 TLE 的边缘疯狂试探)
所以我们可以思考优化一下,把均摊复杂度降低,我们尝试使每块前大后小(大小等差为奇奇怪怪的)问题,取近似值
二、代码
#include<bits/stdc++.h>//注意这里的数据范围明显超过了int,所以要开LL
#define LL long long
#define N 1000010
#define s_N 1010
using namespace std;
LL A[N],siz,B[s_N],S[s_N];//B为分块中的和,S为分块边界
int n,L,v;
inline bool cmp(int a,int b){return a>b;}
int solve(LL a)//解决问题
{
LL sum=L;
if(sum>a*v) return 0;
for(int i=1;i<=siz;i++)
{
sum+=B[i];//累加一整块
if(sum>a*v)//答案在刚刚累加的块里
{
sum-=B[i];
for(int j=S[i-1];j<S[i];j++)//在块里逐个累加,知道找到准确答案
{
sum+=A[j];
if(sum>a*v) return j;
}
}
}
return -1;
}
void get_blank()//分块,并给每个块做累加
{
int sum=0;
for(int i=ceil(sqrt(2*n));sum<=n;i--)
{
S[siz++]=sum+1;
sum+=i;
}
S[siz]=n+1;
for(int i=1;i<=siz;i++)
for(int j=S[i-1];j<S[i];j++)
B[i]+=A[j];
}
int main()
{
cin >> n >> L >> v;
for(int i=1;i<=n;i++) scanf("%lld",A+i);
sort(A+1,A+n+1,cmp);
get_blank();
int q;
cin >> q;
while(q--)
{
LL a;
scanf("%lld",&a);
cout << solve(a) << '\n';
}
return 0;
}