codeforces940E 2000分dp

题目传送门

题意:

n个数,第 i 个数是 a_i 。还有一个正整数 c 。

\dpi{150}n 个数分成任意段,每一段把段内最小的 \left \lfloor \frac{len}{c} \right \rfloor 个数去掉。 len 是该段内数的个数, c 是题目给定的数。

问剩余数之和的最小值。

数据范围:1 \leqslant n , c \leqslant 10^5 , 1\leqslant a_i \leqslant 10^9 。

题解:

dp[i] 表示前 i 个数分成任意段的最小值。

sum[i] 表示前 i 个数的和。

query(l,r) 计算 [l,r] 区间的最小值,ST表维护即可。

转移方程:dp[i] = min(dp[i - 1] + a[i] , dp[i - c] + sum[i] - sum[i - c] - query(i - c + 1 , i)) 。

感受:

一眼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 ;
}
发布了215 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104118375
今日推荐