- 1000ms
- 262144K
给定一个长度为 nn 的序列 aa,以及一个整数 cc。
一个长度为 kk 的序列的值为序列中除了最小的 \lfloor \frac k c \rfloor⌊ck⌋ 个元素之外的所有元素之和。
例如 c = 2c=2,[3,1,6,5,2][3,1,6,5,2] 的值为 3+6+5=143+6+5=14。
现在你需要将数组 aa 划分成若干个连续的子序列,求所有划分方案中子序列的值之和的最小值。
输入格式
第一行两个整数 n, cn,c。
第二行 nn 个整数,表示序列 aa。
输出格式
输出一行一个整数,表示所有划分方案中子序列值之和的最小值。
数据范围
对于 40\%40% 的数据,1 \leq n, c \leq 10^31≤n,c≤103
对于 100\%100% 的数据,1 \leq n, c \leq 10^51≤n,c≤105
对于 100\%100% 的数据,1 \leq a[i] \leq 10^91≤a[i]≤109
样例输入复制
12 10 1 1 10 10 10 10 10 10 9 10 10 10
样例输出复制
92
思路:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int lg[N] = {-1};
int minn[N][20];
int data[N];
long long dp[N];
long long sum[N];
int _pow(int a)
{
return 1 << a;
}
int query(int l, int r)//可以有重复区间
{
int len = lg[r-l+1];
int a = minn[l][len];
int b = minn[r-_pow(len)+1][len];
return data[a] < data[b] ? data[a] : data[b];
}
int main()
{
int n,c;
cin>>n>>c;
for(int i=1; i<=n; i++)
{
scanf("%d", &data[i]);
sum[i] = sum[i-1] + data[i];
lg[i] = lg[i/2] + 1;
minn[i][0] = i;
}
for(int j=1; _pow(j)<=n; j++)
for(int i=1; i+_pow(j)-1<=n; i++)
{
int a = minn[i][j-1];
int b = minn[i+_pow(j-1)][j-1];
minn[i][j] = data[a] < data[b] ? a : b;
}
for(int i=1; i<=n; i++)
{
if(i < c) dp[i] = sum[i];
else
{
dp[i] = min(dp[i-1] + data[i], dp[i-c] + sum[i] - sum[i-c] - query(i - c + 1, i));
}
}
cout<<dp[n];
return 0;
}