题目简略描述:给定正整数,长为
的正整数数列
满足限制
,记
为
中
的个数,求
解答:记,利用集合的无序性,有如下引理
引理1:任意
的
,都能找到一个(除去0之后)单调递减的
同样有
.
不妨设
单调递增,由于
单调递增,由排序不等式,上式
,其中
单调递减.此时只改变了
的顺序,换言之,
不变(
),因此仍然保持
,但是求和比原来小了
,由于
单调递减且
,有
,那么将多出的
加到最大值
上即可得到一个同样符合求和为
的
,此时对应的
满足去0后单调递减,或者说去掉索引为
的
后单调递减
以上引理表明,我们总能认为是单调递减的,因为其他情况都可以转换到此且mex不变.最优解的
也是如此。并且,由于我们排序后只改变了最大元素
,因此
是
或者
的形状
我们考虑,这等价于
且
。我们发现
中大于
的元素都是没用上的(如果有的话)。如果有大于
的元素,由于去0后单调递减,那么有
,将
数量减少到
,并类似的将多的
加到最大元素
上,于是得到
的构造,再利用引理做排序,如果
还有大于
的元素,重复上述过程。
最终我们得到中没有大于
的元素,换言之,
,也即
。而由于
单调递减,于是可以断定
,其中
,而第
个元素为
,且
.因此,我们对
的RHS求和得到
并且
.基于此基本就可以做二分法了,对
在值域上二分,判断
得到的
是否大于等于
,符合就表明此时的k是一个可行解。
(这里能二分的原因是上面构造保证了小于最优解的构造都存在;或者说二分判断条件是单调的)
提供一个二分的代码:
int main() {
int t;
cin >> t;
while (t--) {
long long m;
cin >> m;
unsigned long long ans = 0;
long long l = 1, r = 2e6;
long long mid = (l + r) / 2;
while (l +1< r) {
mid = (l + r) / 2;
long long a = m - (mid - 1) * (mid - 2) * (mid + 3) / 6;
if (a >= mid - 1) {
ans = mid;
l = mid;
} else r = mid;
}
cout << ans << endl;
}
}
其中有需要注意的一些点,一个显然的是a有负值,不能用unsigned;另外一个比较关键的是r的估计,由于,从上面的计算可以知道,显然
是在1e6量级的,但是如果直接
r=1e6过不了。当然可以调整出r=2e6可以过,不过我们也可以做一个更好的估计然后不使用二分,直接遍历:
,我们可以得到
的一个估计:
于是。有了这个估计就可以直接遍历
int main() {
int t;
cin >> t;
while(t--){
long long m;
cin >> m;
long long k=cbrt(6*m)+5;
while(true){
long long a = m - (k-1)*(k-2)*(k+3)/6;
if(a>=k-1) break;
k--;
}
cout << k << endl;
}
}
当然,如果注意到上界,还能做到
的算法,
如果足够大,可以分配给1~k-1使得其都多一个,并且自己还能保留大于等于
的数量,那么就得到了
的构造。因此,最优的情况应该要求
于是就有
于是,综合得到
,可以计算m在R上区间长度值域在
之间,因此可以做到11次判断就得到答案(比二分的
略少一些并且不用管
的大小)
将先前二分的l,r改为l = cbrt(6*(m-1))-3, r = cbrt(6*m)+6即可。
此外,顺带一提,手动测试了一下,l = cbrt(6*(m-1))-1, r = cbrt(6*m)+2也能AC本题,区间长度减少了6,只是不确定是不是严格的上下界,可能是数据比较弱(?)

京公网安备 11010502036488号