给定长为 的数字序列 。
次询问,每次询问给定一个 ,问能把序列最少划分成多少个和不超过 的连续段,无解输出Impossible.
是一个重要的条件,因为分好段后连续两段的和一定大于 ,所以序列最多分成 段。
如果能在 内做完一次查询,那么根据 ,问题就可以在 内解决。
可以吗?当然可以,做前缀和,然后二分查找下一个分段点,单次复杂度 .
在 很小的时候,这样的做法比较亏,因为可以使用 的暴力。
所以总的复杂度:
,但是常数非常的小,最后只跑了156ms.
总结:
- 不要漏掉任何一个数据范围或条件。
- 对于不同的分段依据,可以使用 这个式子来解题
- 对于不同的数据范围,可以使用不同的方法去处理。
- 写好伪代码(或流程)再上机效率极高。
- 队友nb.
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 1000016, MOD = 1000000007;
int save[M], num[M], sum[M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read(), mx = 0;
for(int i=1; i<=n; ++i)
{
num[i] = read();
sum[i] = sum[i-1] + num[i];
mx = max(mx, num[i]);
}
memset(save, -1, sizeof(save));
int q = read();
while(q--)
{
int t = read();
if(save[t]!=-1)
{
printf("%d\n",save[t] );
continue;
}
if(t<mx)
{
printf("Impossible\n");
continue;
}
int ans = 0;
if(t<20)
{
for(int now=0, cur = 1; cur<=n; ++cur)
{
if(now+num[cur]>t) ++ans, now=0;
now += num[cur];
}
++ans;
}
else
{
for(int cur=0; cur<n; )
{
++ans;
cur = upper_bound(sum+cur+1, sum+n+1, sum[cur]+t) - sum -1;
}
}
save[t] = ans;
printf("%d\n",ans );
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}