欸嘿嘿嘿嘿嘿嘿

Preface: 其实原来的思路挂了。看 #7 数据才有新的思路。


首先有一个贪心的思路:先不管那么多,在前面尽量从 \(1\) 开始顺序填充,把能放的 \([1,p]\) 先放了。

显然这样的方法在同等长度内是最优解。其实是没看出来怎么证。

手玩可得,若 \(2\mid p\),对三元组的数量贡献为 \(\frac{p(p-2)}{4}\) ;若 \(2\nmid p\),对三元组的数量贡献为 \(\frac{(p-1)^2}{4}\)

这样的话,若有 \(m>\frac{p(p-2)}{4}\)\(m>\frac{(p-1)^2}{4}\),可以确定无解。

\(n=15,m=39\) 为例。

\(2\nmid n\)\(\frac{(13-1)^2}{4}=36<m\) ,故 \(p=13\) 为满足条件的最大值。

\(a=\{1,2,3,4,5,6,7,8,9,10,11,12,13\}\)

接下来会剩余一些需要满足的三元组。

如果直接用 \(a_{n+1}:=a_{n-1}+a_n\) 来套的话,\(a\) 的增长速度是 \(2\) 倍,显然不可接受。

那就说明 \(a\) 的内部还有一些操作空间。

感谢 #7 测试点送的思路

观察发现评测姬给出的答案是 \(a=\{1,2,\dots13,20,34\}\)。显然 \(34\) 与答案无关。而 \(20=7+13=8+12=9+11\)

那么就有 \(\forall x\in[p+1,2p-1]\),对答案的贡献为 \(a\) 中任选两个值相加等于 \(x\) 的个数。

再观察一下,发现 \(\forall l\in[1, p-1]:(l+p)\in[p+1,2p-1]\)

然后区间 \([l,p]\) 内有 \(\lfloor\frac{p-l+1}{2}\rfloor\)\(i\) 满足 \(a_i+a_{p-i+1}=l+p\)

\(x=l+p\),则其对三元组数量产生的贡献为 \(\lfloor\frac{p-l+1}{2}\rfloor\)

然后聪明的你一定可以想到枚举 \(l\in[1,p-1]\) 然后计算答案

当然,如果上述步骤中 \(a\) 的大小超过了 \(n\),可以视作无解。

显然上述策略是在 \(p\) 一定的情况下对三元组数量贡献最大的。

这时候可能会存在三元组数量还没有到 \(m\) 的情况。

但是由于前面把 \(a\) 内部的元素处理完毕,所以最多也就只剩一个了。

\(a_{n-1}+a_{n}\) 加入结果即可。

但是如果 \(a\) 的大小还没有达到 \(n\),怎么办?

很简单。从 \(10^9\) 倒序往里面扔数。每个数的大小差值为 \(5000\)

由于 \(n=5000\) 时能够满足的最大 \(m=\frac{p(p-2)}{4}=6247500<10^9-5000\times5000\),所以即使全程从 \(10^9\) 倒序往里面扔数,前面的数也不会对其产生任何影响。

大致流程就是这样。放代码。我也不知道为什么长得那么丑

#include <bits/stdc++.h>
#pragma GCC optimize(3, "Ofast", "inline")
int n, k, m;
int last, pos;
std::vector <int> res;
int main() {
	scanf("%d %d", &n, &k), last = k;
    // 判断是否有解
	if(
		((n & 1) && k > (n - 1) * (n - 1) >> 2) ||
		(!(n & 1) && k > n * (n - 2) >> 2)
	) { printf("-1"); return 0; } 
    // 贪心填充前缀 [1,p] 区间
	for(int i = n; i >= 1; i--) {
		int tmp;
		if(i & 1) tmp = (i - 1) * (i - 1) >> 2;
		else tmp = i * (i - 2) >> 2;
		if(tmp <= k) { m = i, last -= tmp, n -= m; break; }
	}
	for(int i = 1; i <= m; i++) res.push_back(i);
    // 枚举区间 [l,p],累加贡献
	for(int l = 1; l < m && last > 0; l++) {
		int tmp, len = m - l + 1;
		tmp = len >> 1;
		if(last >= tmp) last -= tmp, n--, res.push_back(l + m);
	}
    // 
	while(1) {
		if(n < 0) { printf("-1"); return 0; }
		if(last <= 0) break;
		res.push_back(*(res.end() - 1) + *(res.end() - 2));
		n--, last--;
	}
    // 用大数填充 res.size() < n 的部分
	while(n > 0) res.push_back(1e9 - n * 5001), n--;
	std::sort(res.begin(), res.end());
	for(int u : res) printf("%d ", u);
	return 0;
}
posted @   CQWDX  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示