题目传送门
题意:
有个数,第 个数是 。还有一个正整数 。
个数分成任意段,每一段把段内最小的 个数去掉。 是该段内数的个数, 是题目给定的数。
问剩余数之和的最小值。
数据范围: , 。
题解:
表示前 个数分成任意段的最小值。
表示前 个数的和。
计算 区间的最小值,ST表维护即可。
转移方程: 。
感受:
一眼dp,然后就没有然后了。
这么简单的题越想越复杂,感觉总是想不到正确的思路上。
我好弱啊。
扫描二维码关注公众号,回复:
9153222 查看本文章
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e5 + 5 ;
int n , c , a[maxn] ;
int st[maxn][25] ;
ll sum[maxn] , dp[maxn] ;
void init()
{
for(int i = 1 ; i <= n ; i ++) st[i][0] = a[i] ;
for(int j = 1 ; j <= 20 ; j ++)
for(int i = 1 ; i + (1 << j) - 1 <= n ; i ++)
st[i][j] = min(st[i][j - 1] , st[i + (1 << (j - 1))][j - 1]) ;
}
int query(int l , int r)
{
int len = log2(r - l + 1) ;
return min(st[l][len] , st[r - (1 << len) + 1][len]) ;
}
int main()
{
scanf("%d%d" , &n , &c) ;
for(int i = 1 ; i <= n ; i ++) scanf("%d" , &a[i]) ;
sum[0] = 0 ;
for(int i = 1 ; i <= n ; i ++) sum[i] = sum[i - 1] + a[i] ;
init() ;
for(int i = 1 ; i <= n ; i ++)
{
ll y = 1e18 ;
dp[i] = dp[i - 1] + a[i] ;
if(i - c >= 0)
y = dp[i - c] + sum[i] - sum[i - c] - query(i - c + 1 , i) ;
dp[i] = min(dp[i] , y) ;
}
printf("%lld\n" , dp[n]) ;
return 0 ;
}