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;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822484.html