Problem
给定序列和q次询问,每次询问给定x,求最少能将序列分成多少段使得每段的和不超过x。n<=2e5,q<=1e5。
Solution
- 倍增查找前缀和(不想写二分)
- 预处理出前缀和,对于每组询问在前缀和上倍增的查找下一次分割的位置,一些博客上说时间复杂度是个什么调和级数,效率应该在n和logn之间。可记忆化。
- 场上想到过,脑子一抽以为这样做比暴力还差。。。转而去想常数优化。
Code
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
int n,a[maxn*2],qn,q[maxn*10],mx,nw;
int bzsearch(int x)
{
int pos=0,ans=0;
while(pos<n)
{
int poss=pos;
for(int i=nw;i>=0;i--)
{
if(pos+(1<<i)<=n&&a[pos+(1<<i)]-a[poss]<=x)
pos+=(1<<i);
}
ans++;
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
mx=max(mx,a[i]);
a[i]+=a[i-1];
}
while((1<<nw)<=n) nw++;
nw--;
scanf("%d",&qn);
for(int i=1,x;i<=qn;i++)
{
scanf("%d",&x);
if(x<mx)
{
cout<<"Impossible"<<endl;
continue;
}
if(q[x])
cout<<q[x]<<endl;
else{
q[x]=bzsearch(x);
cout<<q[x]<<endl;;
}
}
return 0;
}