• 2023牛客OI赛前集训营-提高组(第三场)C.分糖果


    2023牛客OI赛前集训营-提高组(第三场)C.分糖果


    C-分糖果_2023牛客OI赛前集训营-提高组(第三场) (nowcoder.com)

    题目大意

    求前 i ( i ∈ [ 1 , n ] ) i(i\in[1, n]) i(i[1,n]) 个数分成 k k k 个连续的区间,每一个区间和的最大值最小是多少。

    T T T 组数据

    对于 30 p t s 30pts 30pts 1 ≤ n ≤ 100 , 1 ≤ k ≤ n 1 \le n \le 100 , 1\le k \le n 1n100,1kn

    另外 20 p t s 20pts 20pts 1 ≤ n ≤ 1 0 4 , k = 1 1\le n \le 10^4 , k = 1 1n104,k=1

    另外 50 p t s 50pts 50pts 1 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ n 1\le n \le 10^5 , 1\le k \le n 1n105,1kn

    对于全部数据有 T ≤ 3 , ∣ a i ∣ ≤ 1 0 9 , 1 ≤ n ≤ 1 0 5 T \le 3 , |a_i| \le 10^9 , 1\le n \le 10^5 T3,ai109,1n105

    做法

    考试忘记加换行, 50 p t s → 0 50pts \to 0 50pts0

    对于 30 p t s 30pts 30pts

    直接二分答案 + d p dp dp O ( n 2 ) O(n^2) O(n2) 判断能不能分成大于等于 k k k 块的和满足假设。

    对于 20 p t s 20pts 20pts

    直接前缀和乱搞

    50 p t s 50pts 50pts 代码

    #include 
    #define fu(x , y , z) for(int x = y ; x <= z ; x ++)
    #define LL long long
    using namespace std;
    const int N = 1e5 + 5 , M = 1e5 + 5;
    const LL inf = 1e14 + 5;
    int n , k;
    LL a[M] , f[M] , s[M] , ans;
    bool ck (LL x) {
        int l = 1;
        fu (i , 1 , n) f[i] = 0;
        fu (i , 1 , n) {
            if (i == 1) {
                f[i] = (s[1] <= x);
            }
            else {
                fu (j , 1 , i) {
                    if (s[i] - s[j - 1] <= x && (f[j - 1] || j == 1)) f[i] = max (f[i] , f[j - 1] + 1);
                }
            }
            if (f[i] >= k) return 1;
        }
        return 0;
    }
    LL fans (LL l , LL r) {
        if (l == r) {
            return ck (l) ? l : inf;
        }
        LL mid = l + r >> 1;
        if (ck (mid)) return min (fans (l , mid) , mid);
        else return min (inf , fans (mid + 1 , r));
    }
    int main () {
        // freopen ("candy2.in" , "r" , stdin);
        int T; 
        scanf ("%d" , &T);
        while (T --) {
            scanf ("%d%d" , &n , &k);
            fu (i , 1 , n) scanf ("%lld" , &a[i]) , s[i] = 0;
            fu (i , 1 , n) s[i] = s[i - 1] + a[i];
            if (k == 1) {
                ans = inf;
                fu (i , 1 , n) ans = min (ans , s[i]);
                printf ("%lld\n" , ans);
                continue;
            }
            printf ("%lld\n" , fans (-inf , inf));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    对于 100 p t s 100pts 100pts

    权值线段树 + 离散化

    我们发现上面的 d p dp dp 转移太慢了

    s s s 数组表示前缀和 ,当前二分答案为 x x x

    对于 i ∈ [ 1 , n ] i\in[1 , n] i[1,n] 我们只用找到 j ∈ [ 1 , i ] j\in[1 , i] j[1,i] 满足 s [ i ] − s [ j ] ≤ x s[i] - s[j] \le x s[i]s[j]x s [ i ] − x ≤ s [ j ] s[i] - x \le s[j] s[i]xs[j] 时的最大值 + 1 +1 +1


    f [ i ] = M A X j = 1 i f [ j ] + 1 ( s [ i ] − x ≤ s [ j ] ) f[i] = MAX_{j = 1}^i f[j] + 1(s[i] - x \le s[j]) f[i]=MAXj=1if[j]+1(s[i]xs[j])
    用权值线段树维护就好了。

    初始化 f [ 0 ] = 0 f[0] = 0 f[0]=0

    每次把 f [ i ] f[i] f[i] 插入权值线段树中

    因为总和太大了,所以还要把 s [ i ] s[i] s[i] s [ i ] − x s[i] - x s[i]x 拿出来离散化(还有 0 0 0) ,动态开点。

    #include 
    #define fu(x , y , z) for(int x = y ; x <= z ; x ++)
    #define LL long long
    using namespace std;
    const int N = 4e7 + 5 , M = 2e5 + 5;
    const LL inf = 1e9 * 1e6;
    const int inff = 1e9 + 5;
    int n , k , cnt , mp[M];
    LL a[M] , f[M] , s[M] , ans , ss[M << 1];
    struct Tr {
        int v , lp , rp;
    } tr[N];
    void glp (int p) {
        if (!tr[p].lp) {
            tr[p].lp = ++cnt;
            tr[cnt].v = -inff;
        }
    }
    void grp (int p) {
        if (!tr[p].rp) {
            tr[p].rp = ++cnt;
            tr[cnt].v = -inff;
        }
    }
    void change (int p , LL l , LL r , int x , int val) {
        if (l == r) tr[p].v = max (tr[p].v , val);
        else {
            int mid = l + r >> 1;
            if (x <= mid) {
                glp (p);
                change (tr[p].lp , l , mid , x , val);
            }
            else {
                grp (p);
                change (tr[p].rp , mid + 1 , r , x , val);
            }
            tr[p].v = max (tr[tr[p].lp].v , tr[tr[p].rp].v);
        }
    }
    int query (int p , LL l , LL r , LL L , LL R) {
        if (L <= l && R >= r) return tr[p].v;
        else {
            int mid = l + r >> 1 , ans1 = -inff , ans2 = -inff;
            if (L <= mid && tr[p].lp) 
                ans1 = query (tr[p].lp , l , mid , L , R);
            if (R > mid && tr[p].rp) 
                ans2 = query (tr[p].rp , mid + 1 , r , L , R);
            return max (ans1 , ans2);
        }
    }
    void cl (int p) { tr[p].lp = tr[p].rp = 0 , tr[p].v = -inff; }
    void clear (int p , LL l , LL r) {
        if (l == r) {
            cl (p);
            return;
        }
        int mid = l + r >> 1;
        if (tr[p].lp) 
            clear (tr[p].lp , l , mid);
        if (tr[p].rp)
            clear (tr[p].rp , mid + 1 , r);
        cl (p);
    } 
    bool ck (LL x) {
        int s1 = 1;
        ss[1] = 0;
        fu (i , 1 , n) {
            ss[++s1] = s[i];
            ss[++s1] = s[i] - x;
        }
        sort (ss + 1 , ss + s1 + 1);
        int m = unique (ss + 1 , ss + s1 + 1) - ss - 1;
        clear (1 , -M , M);
        tr[0].v = -M;
        cnt = 1;
        int y = lower_bound(ss + 1 , ss + s1 + 1 , 0) - ss;
        change (1 , -M , M , y , 0);
        fu (i , 1 , n) {
            y = lower_bound(ss + 1 , ss + s1 + 1 , s[i] - x) - ss;
            f[i] = query (1 , -M , M , y , M) + 1;
            y = lower_bound(ss + 1 , ss + s1 + 1 , s[i]) - ss;
            change (1 , -M , M , y , f[i]);
            if (f[i] >= k) return 1;
        }
        return 0;
    }
    LL fans (LL l , LL r) {
        if (l == r) return ck (l) ? l : inf;
        else {
            LL mid = l + r >> 1;
            if (ck (mid))  {
                return min (fans (l , mid) , mid);
            }
            else {
                return fans (mid + 1 , r);
            }
        }
    }
    int main () {
        // freopen ("candy2.in" , "r" , stdin);
        int T; 
        scanf ("%d" , &T);
        while (T --) {
            scanf ("%d%d" , &n , &k);
            fu (i , 1 , n) scanf ("%lld" , &a[i]) , s[i] = 0;
            fu (i , 1 , n) s[i] = s[i - 1] + a[i];
            printf ("%lld\n" , fans (-inf , inf));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
  • 相关阅读:
    简历信息粘贴板
    《算法竞赛·快冲300题》每日一题:“矩阵”
    集合-set系列集合
    httpclient作用及其使用
    Kerberos协议详解
    在 Maui 中自绘组件1:绘制
    原子操作类
    golang学习之路2-基础认识(上)
    C++11之继承构造函数(using 声明)
    如何在Window系统部署VisualSVN服务并结合cpolar实现无公网ip远程访问
  • 原文地址:https://blog.csdn.net/fzyyyyyyyy/article/details/133686421