題目傳送門
題意:
有個數,第 個數是 。還有一個正整數 。
個數分成任意段,每一段把段內最小的 個數去掉。 是該段內數的個數, 是題目給定的數。
問剩餘數之和的最小值。
數據範圍: , 。
題解:
表示前 個數分成任意段的最小值。
表示前 個數的和。
計算 區間的最小值,ST表維護即可。
轉移方程: 。
感受:
一眼dp,然後就沒有然後了。
這麼簡單的題越想越複雜,感覺總是想不到正確的思路上。
我好弱啊。
代碼:
#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 ;
}