zoukankan      html  css  js  c++  java
  • Npc51 E 数列 [构造题, 二分答案]

    数列

    题目描述见链接 .


    color{red}{正解部分}

    首先考虑怎么划分序列,
    可以想到: 最优划分方法 会将 序列 分为连续的几段 上升序列 .

    若存在下降序列, 可以将其倒置, 答案会更优 .

    设划分了 cntcnt上升序列, 则 显然 ans=Ncntans = N-cnt,

    现在要使得 ansans 尽可能大, 就要使得 cntcnt 尽量小,

    考虑 cntcnt 受什么限制, 受 MM 的大小限制,
    具体来说: 设每个 上升序列 的长度为 xix_i, 则受到的限制为 sum=(1+xi)xi2Msum = sum frac{(1+x_i)x_i}{2} le M .

    此时可以想到, 若 cntcnt 可以合法地构造, 那么 cnt+1cnt+1 也一定可以合法地构造, 换句话说, cntcnt 具有单调性 .

    cntcnt上升序列 中的一段上升序列分割为两个, sumsum 只会更小, 所以不会不合法 .

    于是 二分答案, 二分 cntcnt, 考虑如何 check()check(),

    sumsum 最小值大于 MM 时, 说明 cntcnt 一定不合法, 依此进行判断,

    考虑 cntcnt 固定的时候, sumsum 什么时候最小, 显然当每个 xix_i 都尽可能均匀时, sumsum 最小 .

    设最大的 xix_ixmaxx_{max}, 最小的 xix_ixminx_{min}, 则当把 xmaxx_{max}长度 减 11, 把 xminx_{min} 长度 加 11,
    sumsum 的变化值为 xmax+(xmin+1)-x_{max} + (x_{min}+1), 其中 xmin+1xmaxx_{min}+1 le x_{max}, 所以这么操作 sumsum 不会更大 .

    于是对每个 xix_i 赋初值 Ncntfrac{N}{cnt}, 剩下 N%iN \% i 均匀散布到前面 cntcnt 个长度为 Ncntfrac{N}{cnt}xix_i 中,
    得到 xix_i, 计算 sumsum, 判断是否小于等于 MM 即可 .

    时间复杂度 O(logn+n)O(log n + n)


    color{red}{实现部分}

    #include<bits/stdc++.h>
    #define reg register
    typedef long long ll;
    
    const int maxn = 1e5 + 5;
    
    int N;
    int M;
    int min_cnt;
    
    bool chk(int cnt){
            int x_1 = N/cnt, x_2 = N/cnt + 1;
            int num_1 = cnt - N%cnt, num_2 = N%cnt;
            ll s = (1ll+x_1)*x_1/2*num_1 + (1ll+x_2)*x_2/2*num_2;
            return s <= 1ll*M;
    }
    
    int main(){
            scanf("%d%d", &N, &M);
            int l = 1, r = N; min_cnt = N;
            while(l <= r){
                    int mid = l+r >> 1;
                    if(chk(mid)) min_cnt = std::min(mid, min_cnt), r = mid - 1;
                    else l = mid + 1;
            }
            int x_1 = N/min_cnt, x_2 = N/min_cnt + 1;
            int num_1 = min_cnt - N%min_cnt, num_2 = N%min_cnt;
            for(reg int i = 1; i <= num_1; i ++)
                    for(reg int j = 1; j <= x_1; j ++) printf("%d ", j);
            for(reg int i = 1; i <= num_2; i ++)
                    for(reg int j = 1; j <= x_2; j ++) printf("%d ", j);
            return 0;
    }
    
  • 相关阅读:
    小程序_递归求年纪
    小程序_递归求阶层
    把字符串复制到剪贴板
    主流的三种RF方案及其优缺点比较
    CC1100E的ESD指标?
    delphi怎样打开一个文本文件
    CC1100模块 250K的速率的问题
    delphi 中显示access数据库表怎么实现
    delphi与Access数据库连接的步骤
    数据库基本语句
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822484.html
Copyright © 2011-2022 走看看