题目链接:点击查看
题目大意:给出一个长度为 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;}elser=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");elseprintf("%d\n",solve(x));}return 0;
}