欸嘿嘿嘿嘿嘿嘿
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】