题目描述见链接 .
首先考虑怎么划分序列,
可以想到: 最优划分方法 会将 序列 分为连续的几段 上升序列 .
若存在下降序列, 可以将其倒置, 答案会更优 .
设划分了 段 上升序列, 则 显然 ,
现在要使得 尽可能大, 就要使得 尽量小,
考虑 受什么限制, 受 的大小限制,
: 设每个 上升序列 的长度为 , 则受到的限制为 .
此时可以想到, 若 可以合法地构造, 那么 也一定可以合法地构造, 换句话说, 具有单调性 .
将 段 上升序列 中的一段上升序列分割为两个, 只会更小, 所以不会不合法 .
于是 二分答案, 二分 , 考虑如何 ,
最小值大于 时, 说明 一定不合法, 依此进行判断,
考虑 固定的时候, 什么时候最小, 显然当每个 都尽可能均匀时, 最小 .
设最大的 为 , 最小的 为 , 则当把 长度 减 , 把 长度 加 ,
的变化值为 , 其中 , 所以这么操作 不会更大 .
于是对每个 赋初值 , 剩下 均匀散布到前面 个长度为 个 中,
得到 , 计算 , 判断是否小于等于 即可 .
时间复杂度
#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;
}