Codeforces Round #466 (Div. 2) E. Cashback (dp+树状数组+RMQ)

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 of length n and an integer c.

The value of some array b of length k is the sum of its elements except for the  smallest. For example, the value of the array [3, 1, 6, 5, 2] with c = 2 is 3 + 6 + 5 = 14.

Among all possible partitions of a into contiguous subarrays output the smallest possible sum of the values of these subarrays.

Input

The first line contains integers n and c (1 ≤ n, c ≤ 100 000).

The second line contains n integers ai (1 ≤ ai ≤ 109) — elements of a.

Output

Output a single integer  — the smallest possible sum of values of these subarrays of some partition of a.

Examples
input
Copy
3 5
1 2 3
output
6
input
Copy
12 10
1 1 10 10 10 10 10 10 9 10 10 10
output
92
input
Copy
7 2
2 3 6 4 5 7 1
output
17
input
Copy
8 4
1 3 4 5 5 3 4 1
output
23
Note

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] with the values 2 and 90 respectively.

In the third example one of the optimal partitions is [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] with the values 1, 21 and 1 respectively.

题意:这个题有点难读,定义一个数组的价值为所有元素和减去最小的k/c(向下取整)个数,这个c是给定的。你可以把整个数组分为多个连续的多个序列,这整个数组的价值就等于所有子数组价值的和。给你一个数组a,你可以把数组a分为多个子数组,问你a的最小价值为多少。

思路:我们可以把数组a分成多个k个一组的,每一组都是去掉一个最小值,但是怎么分才能使得总价值最小呢,那肯定是每一组去掉的那个数加起来的和最大。这样我们可以用dp[i]来表示以第i个数结尾,前面已经分成多组,每一组都去掉值和的最大值,那么转移就是dp[i] = max{ dp[k] + query(i-k+1,i) | (k <= i  - c},这个query是求的一个区间的最小值,可以用RMQ预处理出来,然后dp[k]的最大值可以用树状数组来保存,最后答案就是dp[n]。

-------------------update--------------------

也可以不用树状数组,直接下面这样转移就可以了

dp[c] = query(1,c);
for(i=c+1;i<=n;i++)
{
    dp[i] = max(dp[i-1],dp[i-c] + query(i-c+1,i));
}
--------------------------------------------------------------
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long

using namespace std;

int n;
int minsum[100010][30];
int a[100010];
LL dp[100010];

void RMQ()
{
    int i,j;
    for(i=1;i<=n;i++)
    {
        minsum[i][0] =  a[i];//预处理第一层
    }
    for(j=1;(1<<j)<=n;j++)
        for(i=1;i + (1<<j) - 1 <= n;i++)
            {
                minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
            }
}

int query(int L,int R)
{
    int k = (int)(log(R - L + 1.0)/log(2.0));
    int minn = min(minsum[L][k],minsum[R-(1<<k)+1][k]);
    return minn;
}

void update(int k,LL num)
{
    while(k<=n)
    {
        dp[k] = max(dp[k],num);
        k+=k&-k;
    }
}

LL read(int k)
{
    LL sum=0;
    while(k)
    {
        sum = max(sum,dp[k]);
        k-=k&-k;
    }
    return sum;
}
int main(void)
{
    int c,i,j;
    while(scanf("%d%d",&n,&c)==2)
    {
        memset(dp,0,sizeof(dp));
        LL sum = 0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum += a[i];
        }
        RMQ();
        if(c == 1)
        {
            printf("0\n");
            continue;
        }
        update(c,query(1,c));
        for(i=c+1;i<=n;i++)
        {
            update(i,read(i-c) + query(i-c+1,i));
        }
        cout << sum - read(n) << endl;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值