数 列 数列 数列
题目描述见链接 .
正 解 部 分 \color{red}{正解部分} 正解部分
首先考虑怎么划分序列,
可以想到: 最优划分方法 会将 序列 分为连续的几段 上升序列 .
若存在下降序列, 可以将其倒置, 答案会更优 .
设划分了 c n t cnt cnt 段 上升序列, 则 显然 a n s = N − c n t ans = N-cnt ans=N−cnt,
现在要使得 a n s ans ans 尽可能大, 就要使得 c n t cnt cnt 尽量小,
考虑
c
n
t
cnt
cnt 受什么限制, 受
M
M
M 的大小限制,
具
体
来
说
具体来说
具体来说: 设每个 上升序列 的长度为
x
i
x_i
xi, 则受到的限制为
s
u
m
=
∑
(
1
+
x
i
)
x
i
2
≤
M
sum = \sum \frac{(1+x_i)x_i}{2} \le M
sum=∑2(1+xi)xi≤M .
此时可以想到, 若 c n t cnt cnt 可以合法地构造, 那么 c n t + 1 cnt+1 cnt+1 也一定可以合法地构造, 换句话说, c n t cnt cnt 具有单调性 .
将 c n t cnt cnt 段 上升序列 中的一段上升序列分割为两个, s u m sum sum 只会更小, 所以不会不合法 .
于是 二分答案, 二分 c n t cnt cnt, 考虑如何 c h e c k ( ) check() check(),
s u m sum sum 最小值大于 M M M 时, 说明 c n t cnt cnt 一定不合法, 依此进行判断,
考虑 c n t cnt cnt 固定的时候, s u m sum sum 什么时候最小, 显然当每个 x i x_i xi 都尽可能均匀时, s u m sum sum 最小 .
设最大的 x i x_i xi 为 x m a x x_{max} xmax, 最小的 x i x_i xi 为 x m i n x_{min} xmin, 则当把 x m a x x_{max} xmax长度 减 1 1 1, 把 x m i n x_{min} xmin 长度 加 1 1 1,
s u m sum sum 的变化值为 − x m a x + ( x m i n + 1 ) -x_{max} + (x_{min}+1) −xmax+(xmin+1), 其中 x m i n + 1 ≤ x m a x x_{min}+1 \le x_{max} xmin+1≤xmax, 所以这么操作 s u m sum sum 不会更大 .
于是对每个
x
i
x_i
xi 赋初值
N
c
n
t
\frac{N}{cnt}
cntN, 剩下
N
%
i
N \% i
N%i 均匀散布到前面
c
n
t
cnt
cnt 个长度为
N
c
n
t
\frac{N}{cnt}
cntN 个
x
i
x_i
xi 中,
得到
x
i
x_i
xi, 计算
s
u
m
sum
sum, 判断是否小于等于
M
M
M 即可 .
时间复杂度 O ( log n + n ) O(\log n + n) O(logn+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;
}