题目描述
Since you are the best Wraith King, Nizhniy Magazin «Mir» at the centre of Vinnytsia is offering you a discount.
You are given an array a a a of length n n n and an integer c c c .
The value of some array b b b of length k k k is the sum of its elements except for the smallest. For example, the value of the array [3,1,6,5,2] [3,1,6,5,2] [3,1,6,5,2] with c=2 c=2 c=2 is 3+6+5=14 3+6+5=14 3+6+5=14 .
Among all possible partitions of a a a into contiguous subarrays output the smallest possible sum of the values of these subarrays.
输入格式
The first line contains integers n n n and c c c ( 1<=n,c<=100000 1<=n,c<=100000 1<=n,c<=100000 ).
The second line contains n n n integers ai a_{i} ai ( 1<=ai<=109 1<=a_{i}<=10^{9} 1<=ai<=109 ) — elements of a a a .
输出格式
Output a single integer — the smallest possible sum of values of these subarrays of some partition of a a a .
题意翻译
给你一个长度为n的数列a和整数c
你需要把它任意分段
每一段假设长度为k,就去掉前\(\lfloor\frac{k}{c}\rfloor\) 小的数
最小化剩下的数的和
输入输出样例
输入 #1
3 5
1 2 3
输出 #1
6
输入 #2
12 10
1 1 10 10 10 10 10 10 9 10 10 10
输出 #2
92
输入 #3
7 2
2 3 6 4 5 7 1
输出 #3
17
输入 #4
8 4
1 3 4 5 5 3 4 1
输出 #4
23
说明/提示
In the first example any partition yields 6 as the sum.
In the second example one of the optimal partitions is [1,1],[10,10,10,10,10,10,9,10,10,10] [1,1],[10,10,10,10,10,10,9,10,10,10] [1,1],[10,10,10,10,10,10,9,10,10,10] with the values 2 and 90 respectively.
In the third example one of the optimal partitions is [2,3],[6,4,5,7],[1] [2,3],[6,4,5,7],[1] [2,3],[6,4,5,7],[1] with the values 3, 13 and 1 respectively.
In the fourth example one of the optimal partitions is [1],[3,4,5,5,3,4],[1] [1],[3,4,5,5,3,4],[1] [1],[3,4,5,5,3,4],[1] with the values 1, 21 and 1 respectively.
分析
首先,因为要最小化剩下的数的和,那么我们肯定要使取走的数的总和尽可能大
我们还可以发现,因为要去掉前\(\lfloor\frac{k}{c}\rfloor\) 小的数
因此只有一段区间的长度大于等于\(c\)时才会对结果产生贡献
而且我们划分出长度为\(k\times c + m (1\leq m < c)\)的区间一定不如划分出长度为\(k\times c\)的区间更优
因为这两种情况选出的数字的数量相同,但是在第二种情况中选出数的最小值一定不会比前一种情况更小
但是这样写还是不太好处理,因为要涉及到前\(k\)小的数,所以似乎要用到某些高级数据结构
而这样显然是不好处理的
我们进一步推导会发现,将一个长度为\(k\times c\)的区间划分为\(k\)个长度为\(c\)的区间所产生的结果只会更优
比如下面一个长度为\(8\)的序列,\(c=4\)
\(1、 1 、2 、5 、3 、7、 8、 9\)
如果我们把它划分为长度为\(8\)的序列,那么产生的贡献为\(1+1=2\)
但是如果我们把它分成两个长度为\(4\)的序列
\(1、1、2、5\)和\(3、7 、8、9\)
那么产生的贡献为\(1+3=4\)
显然后一种更优
因此,我们将原题进一步转换成将一个长度为\(n\)的序列划分为若干长度为\(c\)的序列,使每一个序列的最小值之和最大
其中区间最值可以用线段树去维护
那么我们可以写出如下的状态转移方程
for(int i=1;i<=n;i++){
f[i]=max(f[i],f[i-1]);
if(i>=c) f[i]=max(f[i],f[i-c]+(long long)jl[i]);
}
其中\(f[i]\)表示以遍历到下标为\(i\)的元素所选出的最大价值
\(jl[i]\)表示以\(a[i]\)结尾的长度为\(c\)的区间中的最小值
如果我们选取以\(a[i]\)结尾的长度为\(c\)的区间,那么\(f[i]=max(f[i],f[i-c]+(long long)jl[i]\)
否则\(f[i]=max(f[i],f[i-1])\)
代码
#include
using namespace std;
const int maxn=1e5+5;
int a[maxn];
struct trr{
int l,r,mmin;
}tr[maxn<<2];
void push_up(int da){
tr[da].mmin=min(tr[da<<1].mmin,tr[da<<1|1].mmin);
}
void build(int da,int l,int r){
tr[da].l=l,tr[da].r=r;
if(l==r){
tr[da].mmin=a[l];
return;
}
int mids=(l+r)>>1;
build(da<<1,l,mids);
build(da<<1|1,mids+1,r);
push_up(da);
}
int cx(int da,int l,int r){
if(tr[da].l>=l && tr[da].r<=r){
return tr[da].mmin;
}
int ans=0x3f3f3f3f,mids=(tr[da].l+tr[da].r)>>1;
if(l<=mids) ans=min(ans,cx(da<<1,l,r));
if(r>mids) ans=min(ans,cx(da<<1|1,l,r));
return ans;
}
int jl[maxn];
long long f[maxn];
int main(){
long long tot=0;
int n,c;
scanf("%d%d",&n,&c);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
tot+=(long long)a[i];
}
build(1,1,n);
long long ans=0;
for(int i=1;i<=n-c+1;i++){
jl[i+c-1]=cx(1,i,i+c-1);
}
for(int i=1;i<=n;i++){
f[i]=max(f[i],f[i-1]);
if(i>=c) f[i]=max(f[i],f[i-c]+(long long)jl[i]);
}
printf("%lld\n",tot-f[n]);
return 0;
}