题目链接:点击查看
题目大意:给出一个长度为 n 的数列,再给出 m 次询问,每次询问给出一个阈值 x ,问最少将数列分割成多少段,可以使得每一段的总和都不超过 x,无解的话输出 Impossible
题目分析:首先单独讨论一下无解的情况,因为数列总是可以被分成 n 段,也就是每个元素独立一段,这样的话如果阈值小于最大值的话显然是无解的
如果不考虑时间复杂度的话,不难想到一个 n * m 的做法,就是对于每个询问,都扫一遍数列,然后贪心求解
考虑优化,首先 m 个询问肯定是无法优化的,对于扫一遍数列的时间复杂度 O( n ),考虑优化,因为数列中的每个元素都是正整数,所以维护的前缀和一定是递增的,所以对于每段区间可以进行二分去划分,比如初始时未经过划分的总区间为 [ 1 , n ] ,设阈值为 x ,我们可以二分出一个最大的 pos,使得 sum[ pos ] - sum[ 0 ] <= x,这样可以直接将区间 [ 1 , pos ] 划分为第一段,然后再去给区间 [ pos + 1 , n ] 进行划分,这样做的时间复杂度最坏还是会退化为 O( n ) 的,但考虑利用数组记忆化一下,因为题目中约束了每个询问的阈值一定是小于等于 1e6 的,好像也是在暗示我们要记忆化一样,然后时间复杂度就不太会计算了,暂且当做 O( 能过 ) 吧
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
int n,m,mmax,sum[N],ans[N];
int solve(int x)
{
if(ans[x])
return ans[x];
int last=1;
ans[x]=0;
while(last<=n)
{
ans[x]++;
int l=last,r=n,ans=-1;
while(l<=r)
{
int mid=l+r>>1;
if(sum[mid]-sum[last-1]<=x)
{
l=mid+1;
ans=mid;
}
else
r=mid-1;
}
last=ans+1;
}
return ans[x];
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int num;
scanf("%d",&num);
mmax=max(mmax,num);
sum[i]=sum[i-1]+num;
}
scanf("%d",&m);
while(m--)
{
int x;
scanf("%d",&x);
if(x<mmax)
puts("Impossible");
else
printf("%d\n",solve(x));
}
return 0;
}