zoukankan      html  css  js  c++  java
  • CF940E Cashback 线段树优化DP

    题目描述

    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,就去掉前(lfloorfrac{k}{c} floor) 小的数

    最小化剩下的数的和

    输入输出样例

    输入 #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.

    分析

    首先,因为要最小化剩下的数的和,那么我们肯定要使取走的数的总和尽可能大
    我们还可以发现,因为要去掉前(lfloorfrac{k}{c} floor) 小的数
    因此只有一段区间的长度大于等于(c)时才会对结果产生贡献
    而且我们划分出长度为(k imes c + m (1leq m < c))的区间一定不如划分出长度为(k imes c)的区间更优
    因为这两种情况选出的数字的数量相同,但是在第二种情况中选出数的最小值一定不会比前一种情况更小
    但是这样写还是不太好处理,因为要涉及到前(k)小的数,所以似乎要用到某些高级数据结构
    而这样显然是不好处理的
    我们进一步推导会发现,将一个长度为(k imes 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<bits/stdc++.h>
    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
    ",tot-f[n]);
        return 0;
    }
    
  • 相关阅读:
    POJ 3417 Network
    指针动态开空间的板子
    fread()的板子
    luguo P1970 花匠
    Uva
    Uva
    Uva
    Uva
    Uva
    什么才算是真正的编程能力?
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13339626.html
Copyright © 2011-2022 走看看