题目链接:
题目大意:
给出一个环,每个节点有一个权值,给出每段最大的权值和,问最少划分多少段(每段必须相邻)
题目分析:
- 定义cnt[i]记录以i为最后一组的最后一个的划分的段数,每组肯定是尽可能的多选,那么我们选取的方案就变成了确定的了。
- 定义pre[i]记录以i为最后一组的最后一个的划分方案的左端起点。
- 然后我们的决策方法是:先把环拓展为链,那么就是1~n的数组映射到1+n~2*n,然后每次找到对于以i为当前段最末位的当前段的最左端(区间和小于等于b的最长区间),那么我们就能够知道上一段的最末位j,然后cnt[i] = cnt[j]+1,pre[i]=pre[j]。只要找到i到pre[i]长度大于n的最小情况即可。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAX 2000007
using namespace std;
typedef long long LL;
int n,q;
int a[MAX];
int pre[MAX];
int cnt[MAX];
LL sum[MAX],b;
int main ( )
{
while ( ~scanf ( "%d%d" , &n , &q ))
{
for ( int i = 1 ; i <= n ; i++ )
{
scanf ( "%d" , &a[i] );
a[i+n] = a[i];
pre[i] = i;
}
sum[0] = 0;
for ( int i = 1 ; i <= 2*n ; i++ )
sum[i] = sum[i-1] + a[i];
while ( q-- )
{
scanf ( "%I64d" , &b );
int j = 1;
for ( int i = n+1 ; i <= 2*n ; i++ )
{
while ( sum[i] - sum[j] > b ) j++;
pre[i] = pre[j];
cnt[i] = cnt[j]+1;
if ( i - pre[i] >= n )
{
printf ( "%d\n" , cnt[i] );
break;
}
}
}
}
return 0;
}