题意:这个题有点难读,定义一个数组的价值为所有元素和减去最小的k/c(向下取整)个数,这个c是给定的。你可以把整个数组分为多个连续的多个序列,这整个数组的价值就等于所有子数组价值的和。给你一个数组a,你可以把数组a分为多个子数组,问你a的最小价值为多少。
思路:我们可以把数组a分成多个k个一组的,每一组都是去掉一个最小值,但是怎么分才能使得总价值最小呢,那肯定是每一组去掉的那个数加起来的和最大。这样我们可以用dp[i]来表示以第i个数结尾,前面已经分成多组,每一组都去掉值和的最大值,那么转移就是dp[i] = max{ dp[k] + query(i-k+1,i) | (k <= i - c},这个query是求的一个区间的最小值,可以用RMQ预处理出来,然后dp[k]的最大值可以用树状数组来保存,最后答案就是dp[n]。
-------------------update--------------------
也可以不用树状数组,直接下面这样转移就可以了
dp[c] = query(1,c);
for(i=c+1;i<=n;i++)
{
dp[i] = max(dp[i-1],dp[i-c] + query(i-c+1,i));
}
--------------------------------------------------------------
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int n;
int minsum[100010][30];
int a[100010];
LL dp[100010];
void RMQ()
{
int i,j;
for(i=1;i<=n;i++)
{
minsum[i][0] = a[i];//预处理第一层
}
for(j=1;(1<<j)<=n;j++)
for(i=1;i + (1<<j) - 1 <= n;i++)
{
minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
}
}
int query(int L,int R)
{
int k = (int)(log(R - L + 1.0)/log(2.0));
int minn = min(minsum[L][k],minsum[R-(1<<k)+1][k]);
return minn;
}
void update(int k,LL num)
{
while(k<=n)
{
dp[k] = max(dp[k],num);
k+=k&-k;
}
}
LL read(int k)
{
LL sum=0;
while(k)
{
sum = max(sum,dp[k]);
k-=k&-k;
}
return sum;
}
int main(void)
{
int c,i,j;
while(scanf("%d%d",&n,&c)==2)
{
memset(dp,0,sizeof(dp));
LL sum = 0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum += a[i];
}
RMQ();
if(c == 1)
{
printf("0\n");
continue;
}
update(c,query(1,c));
for(i=c+1;i<=n;i++)
{
update(i,read(i-c) + query(i-c+1,i));
}
cout << sum - read(n) << endl;
}
return 0;
}